{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "d7434284",
   "metadata": {},
   "source": [
    "# 第 3 章：图驱动的 AI 智能体系统\n",
    "\n",
    "> 本笔记文件需要与《LangGraph实战》的第 3 章的内容配套使用。\n",
    "\n",
    "我们习惯于用线性的、因果的视角去理解世界，如同手持一张粗略的地图，在一条条既定的道路上按部就班地前行。然而，真实的疆域远比地图复杂，它充满了未知的岔路、突发的状况，以及无数交织的可能性。当我们试图构建能够理解、适应并驾驭这个复杂世界的 AI 智能体时，线性的思维模式便显得捉襟见肘。**图**，正是这样一种活地图，它超越了线性结构的局限，以节点和边构建起一个充满可能性的网络，让我们得以描绘智能体系统中那些非线性的、动态的、错综复杂的行为路径。\n",
    "\n",
    "本章将深入探讨 LangGraph 的图计算模型，学习如何利用状态、节点、边、命令这四个核心原语构建复杂的智能体系统，掌握并行处理、MapReduce模式、子图机制等高级技术，最终构建出能够应对真实世界复杂挑战的智能体应用。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "environment-setup",
   "metadata": {},
   "source": [
    "### 🚀 环境准备\n",
    "\n",
    "首先加载必要的环境变量配置："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "cell-0",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from dotenv import load_dotenv\n",
    "\n",
    "load_dotenv()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "section-3-1",
   "metadata": {},
   "source": [
    "## 3.1 核心原语：状态、节点、边和命令\n",
    "\n",
    "LangGraph 的核心在于其简洁而强大的图计算模型，这一模型的基石由四个核心原语构成：**状态（State）**、**节点（Node）**、**边（Edge）**，以及**命令（Command）**。\n",
    "\n",
    "理解这四个原语的概念及其相互作用方式，是掌握 LangGraph 并构建复杂智能体系统的重中之重。可以将这四个原语比作乐高积木最基本的、也是最核心的模块，理解了它们，就如同掌握了乐高搭建的\"语言\"，后续才能使用更高级的技巧，搭建出各种各样精巧、复杂、功能强大的智能体系统。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "section-3-1-1",
   "metadata": {},
   "source": [
    "### 3.1.1 状态（State）\n",
    "\n",
    "在 LangGraph 中，状态是贯穿智能体系统运行始终的核心概念。我们可以将其理解为智能体的\"短期记忆\"、\"工作记忆\"或者\"临时共享数据空间\"，它承载着智能体在执行过程中产生的各种信息，例如用户的输入、中间计算结果、工具的输出、对话历史等等。\n",
    "\n",
    "LangGraph 在状态定义上提供了极大的灵活性，允许开发者根据实际应用的需求，选择最合适的数据结构来表示状态。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "example-3-1",
   "metadata": {},
   "source": [
    "##### 示例 3-1：使用 TypedDict 和 Pydantic 定义状态"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "cell-1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "TypedDict 状态示例:\n",
      "{'user_input': 'Hello', 'agent_response': 'Hi there!', 'tool_output': 'weather data'}\n",
      "\n",
      "Pydantic 状态示例:\n",
      "{'user_input': 'Hello', 'agent_response': 'Hi there!', 'tool_output': 'weather data', 'mood': 'happy'}\n"
     ]
    }
   ],
   "source": [
    "from typing_extensions import TypedDict\n",
    "from pydantic import BaseModel, field_validator\n",
    "\n",
    "# 使用 TypedDict 定义状态\n",
    "class TypedDictState(TypedDict):\n",
    "    user_input: str\n",
    "    agent_response: str\n",
    "    tool_output: str\n",
    "\n",
    "# 使用 Pydantic 定义状态，并进行数据验证\n",
    "class PydanticState(BaseModel):\n",
    "    user_input: str\n",
    "    agent_response: str\n",
    "    tool_output: str\n",
    "    mood: str = \"neutral\"  # 默认情绪状态为 neutral\n",
    "\n",
    "    @field_validator('mood')\n",
    "    @classmethod\n",
    "    def validate_mood(cls, value):\n",
    "        if value not in [\"happy\", \"sad\", \"neutral\"]:\n",
    "            raise ValueError(\"情绪状态必须是 'happy', 'sad' 或 'neutral'\")\n",
    "        return value\n",
    "\n",
    "# 测试状态定义\n",
    "print(\"TypedDict 状态示例:\")\n",
    "typed_state = {\"user_input\": \"Hello\", \"agent_response\": \"Hi there!\", \"tool_output\": \"weather data\"}\n",
    "print(typed_state)\n",
    "\n",
    "print(\"\\nPydantic 状态示例:\")\n",
    "pydantic_state = PydanticState(\n",
    "    user_input=\"Hello\", \n",
    "    agent_response=\"Hi there!\", \n",
    "    tool_output=\"weather data\",\n",
    "    mood=\"happy\"\n",
    ")\n",
    "print(pydantic_state.model_dump())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "explanation-3-1",
   "metadata": {},
   "source": [
    "**💡 核心概念解析**：\n",
    "\n",
    "- **TypedDict**：可以快速定义简单的状态结构，提供类型提示但不进行运行时验证\n",
    "- **Pydantic**：提供更强大的数据建模和验证能力，能够在运行时进行数据验证，确保状态的类型和取值符合预期\n",
    "- **状态的作用**：作为各节点间信息传递的桥梁，也是智能体进行决策和行为调整的重要依据"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cab12b8c",
   "metadata": {},
   "source": [
    "##### 示例 3-2：使用多结构体实现私有状态"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "8c172e04",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=== 多结构体状态管理示例 ===\n",
      "\n",
      "初始状态: {'user_input': '查询天气信息', 'agent_response': ''}\n",
      "\n",
      "输入处理节点接收到: 查询天气信息\n",
      "工具节点接收到私有状态: {'api_key': 'secret_api_key_12345', 'tool_config': {'timeout': 30, 'retry_count': 3, 'endpoint': 'https://api.example.com'}, 'user_input': '已处理: 查询天气信息'}\n",
      "初始化 API 客户端，API Key: secret_a..., 配置: {'timeout': 30, 'retry_count': 3, 'endpoint': 'https://api.example.com'}\n",
      "调用 API，输入: 已处理: 查询天气信息\n",
      "\n",
      "最终状态: {'user_input': '已处理: 查询天气信息', 'agent_response': \"基于输入 '已处理: 查询天气信息' 和配置 {'timeout': 30, 'retry_count': 3, 'endpoint': 'https://api.example.com'}，API 返回处理结果\"}\n",
      "\n",
      "=== 执行结果 ===\n",
      "用户输入: 已处理: 查询天气信息\n",
      "智能体响应: 基于输入 '已处理: 查询天气信息' 和配置 {'timeout': 30, 'retry_count': 3, 'endpoint': 'https://api.example.com'}，API 返回处理结果\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/var/folders/f3/zw7t8v4s6j3gd6hyktrdg85r0000gn/T/ipykernel_61919/1411650879.py:54: LangGraphDeprecatedSinceV10: Importing Send from langgraph.constants is deprecated. Please use 'from langgraph.types import Send' instead. Deprecated in LangGraph V1.0 to be removed in V2.0.\n",
      "  from langgraph.constants import Send\n"
     ]
    }
   ],
   "source": [
    "from typing_extensions import TypedDict\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "import json\n",
    "\n",
    "# 定义全局的公共状态 Schema\n",
    "class OverallState(TypedDict):\n",
    "    user_input: str\n",
    "    agent_response: str\n",
    "\n",
    "# 定义节点的私有状态 Schema\n",
    "class ToolState(TypedDict):\n",
    "    api_key: str\n",
    "    tool_config: dict\n",
    "    user_input: str  # 需要包含从公共状态传递的数据\n",
    "\n",
    "# 模拟 API 客户端类\n",
    "class MockAPIClient:\n",
    "    def __init__(self, api_key: str, config: dict):\n",
    "        self.api_key = api_key\n",
    "        self.config = config\n",
    "        print(f\"初始化 API 客户端，API Key: {api_key[:8]}..., 配置: {config}\")\n",
    "\n",
    "    def call_api(self, user_input: str) -> str:\n",
    "        # 模拟 API 调用\n",
    "        response = f\"基于输入 '{user_input}' 和配置 {self.config}，API 返回处理结果\"\n",
    "        print(f\"调用 API，输入: {user_input}\")\n",
    "        return response\n",
    "\n",
    "def create_api_client(api_key: str, tool_config: dict) -> MockAPIClient:\n",
    "    \"\"\"创建 API 客户端的工厂函数\"\"\"\n",
    "    return MockAPIClient(api_key, tool_config)\n",
    "\n",
    "# 定义一个使用私有状态的节点\n",
    "def tool_node(state: ToolState) -> OverallState:\n",
    "    \"\"\"使用私有状态的工具节点\"\"\"\n",
    "    print(f\"工具节点接收到私有状态: {state}\")\n",
    "\n",
    "    # 节点逻辑，例如调用工具 API 并根据 ToolState 中的配置进行操作\n",
    "    api_client = create_api_client(state['api_key'], state['tool_config'])\n",
    "    response = api_client.call_api(state['user_input'])\n",
    "\n",
    "    return {\"agent_response\": response}  # 返回更新后的公共状态\n",
    "\n",
    "# 定义一个输入处理节点\n",
    "def input_processor(state: OverallState) -> OverallState:\n",
    "    \"\"\"处理用户输入的节点\"\"\"\n",
    "    print(f\"输入处理节点接收到: {state['user_input']}\")\n",
    "    processed_input = f\"已处理: {state['user_input']}\"\n",
    "    return {\"user_input\": processed_input}\n",
    "\n",
    "# 定义状态适配节点，将公共状态转换为私有状态\n",
    "def state_adapter(state: OverallState) -> dict:\n",
    "    \"\"\"适配器节点：将公共状态转换为私有状态\"\"\"\n",
    "    from langgraph.constants import Send\n",
    "\n",
    "    # 创建私有状态数据\n",
    "    private_state = {\n",
    "        \"api_key\": \"secret_api_key_12345\",\n",
    "        \"tool_config\": {\n",
    "            \"timeout\": 30,\n",
    "            \"retry_count\": 3,\n",
    "            \"endpoint\": \"https://api.example.com\"\n",
    "        },\n",
    "        \"user_input\": state[\"user_input\"]\n",
    "    }\n",
    "\n",
    "    # 使用 Send 将私有状态发送给工具节点\n",
    "    return Send(\"tool_node\", private_state)\n",
    "\n",
    "# 构建图\n",
    "builder = StateGraph(OverallState)\n",
    "\n",
    "# 添加节点\n",
    "builder.add_node(\"input_processor\", input_processor)\n",
    "builder.add_node(\"state_adapter\", state_adapter)\n",
    "builder.add_node(\"tool_node\", tool_node)\n",
    "\n",
    "# 定义边\n",
    "builder.add_edge(START, \"input_processor\")\n",
    "builder.add_conditional_edges(\"input_processor\", state_adapter, [\"tool_node\"])\n",
    "builder.add_edge(\"tool_node\", END)\n",
    "\n",
    "# 编译图\n",
    "graph = builder.compile()\n",
    "\n",
    "# 测试运行\n",
    "print(\"=== 多结构体状态管理示例 ===\\n\")\n",
    "\n",
    "# 初始状态\n",
    "initial_state = {\n",
    "    \"user_input\": \"查询天气信息\",\n",
    "    \"agent_response\": \"\"\n",
    "}\n",
    "\n",
    "print(f\"初始状态: {initial_state}\\n\")\n",
    "\n",
    "# 运行图\n",
    "try:\n",
    "    final_state = graph.invoke(initial_state)\n",
    "    print(f\"\\n最终状态: {final_state}\")\n",
    "\n",
    "    print(f\"\\n=== 执行结果 ===\")\n",
    "    print(f\"用户输入: {final_state['user_input']}\")\n",
    "    print(f\"智能体响应: {final_state['agent_response']}\")\n",
    "\n",
    "except Exception as e:\n",
    "    print(f\"执行出错: {e}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d4d9df6d",
   "metadata": {},
   "source": [
    "这个完整的示例展示了：\n",
    "\n",
    "1. 多结构体定义：\n",
    "  - `OverallState`：全局公共状态，包含用户输入和智能体响应\n",
    "  - `ToolState`：私有状态，包含 API 密钥、工具配置和用户输入\n",
    "2. 状态转换机制：\n",
    "  - `state_adapter` 节点负责将公共状态转换为私有状态\n",
    "  - 使用 Send API 将私有状态发送给特定节点\n",
    "3. 私有状态的使用：\n",
    "  - `tool_node` 接收私有状态，包含敏感信息（API 密钥、配置）\n",
    "  - 节点处理完成后返回公共状态格式的更新\n",
    "4. 完整的工作流：\n",
    "  - 输入处理 → 状态适配 → 工具调用 → 结果返回"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "740f1a88",
   "metadata": {},
   "source": [
    "##### 示例 3-3：定义输入/输出结构体"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "196d3c49",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "🏗️ 构建 LangGraph（带输入输出结构体约束）...\n",
      "✅ 图构建完成！\n",
      "\n",
      "=== 🚀 输入输出结构体约束示例 ===\n",
      "📥 输入数据 (InputSchema): {'user_query': '什么是人工智能'}\n",
      "🔍 搜索节点: 处理查询 '什么是人工智能'\n",
      "📊 搜索完成，找到 3 条结果\n",
      "🤖 LLM 节点: 基于 3 条搜索结果生成回复\n",
      "💭 LLM 处理完成，生成 80 字符的回复\n",
      "📝 生成回复: 基于您的查询 '什么是人工智能'，我找到了以下信息：搜索结果1: 关于 '什么是人工智能' 的信息 ...\n",
      "\n",
      "📤 输出数据 (OutputSchema): {'llm_response': \"基于您的查询 '什么是人工智能'，我找到了以下信息：搜索结果1: 关于 '什么是人工智能' 的信息 | 搜索结果2: 什么是人工智能 相关数据。希望这能帮到您！\"}\n",
      "📊 输出类型: <class 'dict'>\n",
      "📋 输出键: ['llm_response']\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/var/folders/f3/zw7t8v4s6j3gd6hyktrdg85r0000gn/T/ipykernel_61919/1444540557.py:61: LangGraphDeprecatedSinceV05: `input` is deprecated and will be removed. Please use `input_schema` instead. Deprecated in LangGraph V0.5 to be removed in V2.0.\n",
      "  builder = StateGraph(InternalState, input=InputSchema, output=OutputSchema)\n",
      "/var/folders/f3/zw7t8v4s6j3gd6hyktrdg85r0000gn/T/ipykernel_61919/1444540557.py:61: LangGraphDeprecatedSinceV05: `output` is deprecated and will be removed. Please use `output_schema` instead. Deprecated in LangGraph V0.5 to be removed in V2.0.\n",
      "  builder = StateGraph(InternalState, input=InputSchema, output=OutputSchema)\n"
     ]
    }
   ],
   "source": [
    "from typing_extensions import TypedDict\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "\n",
    "# 定义内部的、全面的状态结构体\n",
    "class InternalState(TypedDict):\n",
    "    user_query: str\n",
    "    search_results: list[str]\n",
    "    llm_response: str\n",
    "    debug_info: str  # 内部调试信息，不需要对外暴露\n",
    "\n",
    "# 定义输入结构体 (只包含 user_query)\n",
    "class InputSchema(TypedDict):\n",
    "    user_query: str\n",
    "\n",
    "# 定义输出结构体 (只包含 llm_response)\n",
    "class OutputSchema(TypedDict):\n",
    "    llm_response: str\n",
    "\n",
    "# 模拟搜索节点\n",
    "def search_node(state: InternalState) -> InternalState:\n",
    "    query = state[\"user_query\"]\n",
    "    print(f\"🔍 搜索节点: 处理查询 '{query}'\")\n",
    "\n",
    "    # 模拟搜索结果\n",
    "    mock_results = [\n",
    "        f\"搜索结果1: 关于 '{query}' 的信息\",\n",
    "        f\"搜索结果2: {query} 相关数据\",\n",
    "        f\"搜索结果3: {query} 详细说明\"\n",
    "    ]\n",
    "\n",
    "    debug = f\"搜索完成，找到 {len(mock_results)} 条结果\"\n",
    "    print(f\"📊 {debug}\")\n",
    "\n",
    "    return {\n",
    "        \"search_results\": mock_results,\n",
    "        \"debug_info\": debug\n",
    "    }\n",
    "\n",
    "# LLM 处理节点\n",
    "def llm_node(state: InternalState) -> InternalState:\n",
    "    query = state[\"user_query\"]\n",
    "    results = state[\"search_results\"]\n",
    "\n",
    "    print(f\"🤖 LLM 节点: 基于 {len(results)} 条搜索结果生成回复\")\n",
    "\n",
    "    # 模拟 LLM 生成响应\n",
    "    combined_info = \" | \".join(results[:2])  # 使用前两条结果\n",
    "    response = f\"基于您的查询 '{query}'，我找到了以下信息：{combined_info}。希望这能帮到您！\"\n",
    "\n",
    "    debug = f\"LLM 处理完成，生成 {len(response)} 字符的回复\"\n",
    "    print(f\"💭 {debug}\")\n",
    "    print(f\"📝 生成回复: {response[:50]}...\")\n",
    "\n",
    "    return {\n",
    "        \"llm_response\": response,\n",
    "        \"debug_info\": state.get(\"debug_info\", \"\") + f\" | {debug}\"\n",
    "    }\n",
    "\n",
    "# 创建图并指定输入输出结构体\n",
    "print(\"🏗️ 构建 LangGraph（带输入输出结构体约束）...\")\n",
    "builder = StateGraph(InternalState, input=InputSchema, output=OutputSchema)\n",
    "\n",
    "# 添加节点\n",
    "builder.add_node(\"search\", search_node)\n",
    "builder.add_node(\"llm\", llm_node)\n",
    "\n",
    "# 定义边\n",
    "builder.add_edge(START, \"search\")\n",
    "builder.add_edge(\"search\", \"llm\")\n",
    "builder.add_edge(\"llm\", END)\n",
    "\n",
    "# 编译图\n",
    "graph = builder.compile()\n",
    "print(\"✅ 图构建完成！\")\n",
    "\n",
    "# 测试运行\n",
    "print(\"\\n=== 🚀 输入输出结构体约束示例 ===\")\n",
    "\n",
    "# 创建符合 InputSchema 的输入\n",
    "input_data = {\"user_query\": \"什么是人工智能\"}\n",
    "print(f\"📥 输入数据 (InputSchema): {input_data}\")\n",
    "\n",
    "# 运行图\n",
    "result = graph.invoke(input_data)\n",
    "\n",
    "# 结果自动符合 OutputSchema 格式\n",
    "print(f\"\\n📤 输出数据 (OutputSchema): {result}\")\n",
    "print(f\"📊 输出类型: {type(result)}\")\n",
    "print(f\"📋 输出键: {list(result.keys())}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "73a0df85",
   "metadata": {},
   "source": [
    "1. 三层状态结构体：\n",
    "  - `InternalState`：内部完整状态，包含所有中间数据\n",
    "  - `InputSchema`：外部输入接口，只需要用户查询\n",
    "  - `OutputSchema`：外部输出接口，只返回最终回复\n",
    "2. 状态封装：\n",
    "  - 内部节点可以访问和修改完整的内部状态\n",
    "  - 外部只能看到定义的输入输出格式\n",
    "  - 调试信息、搜索结果等中间数据被隐藏\n",
    "3. 实际工作流程：\n",
    "  - 搜索节点模拟信息检索\n",
    "  - LLM 节点基于搜索结果生成回复\n",
    "  - 完整的状态管理和数据流\n",
    "4. 输入输出约束验证：\n",
    "  - 输入只需要符合 `InputSchema`\n",
    "  - 输出自动符合 `OutputSchema`\n",
    "  - 内部复杂性被完全封装"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "section-3-1-1-3",
   "metadata": {},
   "source": [
    "#### 3.1.1.3 状态 Reducer \n",
    "\n",
    "状态 Reducer 是 LangGraph 提供的用于自定义状态更新逻辑的核心机制。它允许我们精细地控制状态在节点执行过程中的演变方式，尤其是在处理并发状态更新、复杂数据结构以及需要特定合并策略的场景下。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "example-3-4",
   "metadata": {},
   "source": [
    "##### 示例 3-4：使用状态 Reducer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "cell-2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "演示 add_messages Reducer：\n",
      "初始状态:\n",
      "  HumanMessage: Hello\n",
      "\n",
      "添加新消息:\n",
      "  AIMessage: Hi there! How can I help you?\n",
      "\n",
      "合并后的消息历史:\n",
      "  HumanMessage: Hello\n",
      "  AIMessage: Hi there! How can I help you?\n"
     ]
    }
   ],
   "source": [
    "from typing_extensions import TypedDict, Annotated\n",
    "from operator import add\n",
    "from langgraph.graph.message import add_messages\n",
    "from langchain_core.messages import BaseMessage, HumanMessage, AIMessage\n",
    "\n",
    "# 定义状态 Schema，并为 'message_history' 键指定 add_messages Reducer\n",
    "class ChatState(TypedDict):\n",
    "    message_history: Annotated[list[BaseMessage], add_messages]\n",
    "    user_intent: str\n",
    "    tool_output: str\n",
    "\n",
    "# 演示 add_messages Reducer 的工作方式\n",
    "print(\"演示 add_messages Reducer：\")\n",
    "\n",
    "# 初始状态\n",
    "initial_state = {\n",
    "    \"message_history\": [HumanMessage(content=\"Hello\")],\n",
    "    \"user_intent\": \"greeting\",\n",
    "    \"tool_output\": \"\"\n",
    "}\n",
    "\n",
    "print(\"初始状态:\")\n",
    "for msg in initial_state[\"message_history\"]:\n",
    "    print(f\"  {type(msg).__name__}: {msg.content}\")\n",
    "\n",
    "# 新的消息更新\n",
    "new_messages = [AIMessage(content=\"Hi there! How can I help you?\")]\n",
    "print(\"\\n添加新消息:\")\n",
    "for msg in new_messages:\n",
    "    print(f\"  {type(msg).__name__}: {msg.content}\")\n",
    "\n",
    "# add_messages Reducer 会自动合并消息\n",
    "updated_messages = add_messages(initial_state[\"message_history\"], new_messages)\n",
    "print(\"\\n合并后的消息历史:\")\n",
    "for msg in updated_messages:\n",
    "    print(f\"  {type(msg).__name__}: {msg.content}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "example-3-5",
   "metadata": {},
   "source": [
    "##### 示例 3-5 & 3-6：自定义 Reducer 函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "cell-3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "测试自定义 Reducer：\n",
      "已有列表: ['apple', 'banana', 'orange']\n",
      "新增列表: ['banana', 'grape', 'apple', 'mango']\n",
      "合并去重结果: ['banana', 'grape', 'orange', 'apple', 'mango']\n"
     ]
    }
   ],
   "source": [
    "def reducer_extend_unique(left: list[str] | None, right: list[str] | None) -> list[str]:\n",
    "    \"\"\"\n",
    "    自定义 Reducer 函数，用于合并两个字符串列表，并进行去重\n",
    "    \"\"\"\n",
    "    existing_items = left if left else []  # 如果 left 为 None，则初始化为空列表\n",
    "    new_items = right if right else []     # 如果 right 为 None，则初始化为空列表\n",
    "    combined_items = existing_items + new_items\n",
    "    return list(set(combined_items))       # 使用 set 去重并转换为 list 返回\n",
    "\n",
    "# 测试自定义 Reducer\n",
    "print(\"测试自定义 Reducer：\")\n",
    "\n",
    "existing_list = [\"apple\", \"banana\", \"orange\"]\n",
    "new_list = [\"banana\", \"grape\", \"apple\", \"mango\"]\n",
    "\n",
    "print(f\"已有列表: {existing_list}\")\n",
    "print(f\"新增列表: {new_list}\")\n",
    "\n",
    "result = reducer_extend_unique(existing_list, new_list)\n",
    "print(f\"合并去重结果: {result}\")\n",
    "\n",
    "# 在状态结构体中应用自定义 Reducer\n",
    "class ChatStateWithCustomReducer(TypedDict):\n",
    "    message_history: Annotated[list[BaseMessage], add_messages]\n",
    "    user_intent: str\n",
    "    tool_output: str\n",
    "    item_list: Annotated[list[str], reducer_extend_unique]  # 应用自定义 Reducer\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "section-3-1-1-4",
   "metadata": {},
   "source": [
    "#### 3.1.1.4 Message 与 MessagesState\n",
    "\n",
    "在构建对话型 AI 智能体时，对话历史至关重要。LangGraph 引入了消息（Message）和 MessagesState 的概念，专门优化对话场景的状态管理。\n",
    "\n",
    "LangChain 定义了多种消息类型：\n",
    "- `HumanMessage`：代表人类用户的消息\n",
    "- `AIMessage`：代表 AI 模型生成的消息\n",
    "- `ToolMessage`：代表工具执行后的输出结果消息\n",
    "- `SystemMessage`：代表系统发出的消息"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "example-3-7",
   "metadata": {},
   "source": [
    "##### 示例 3-7：使用 MessagesState 定义状态结构体"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "cell-4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "演示不同类型的消息：\n",
      "1. SystemMessage: 你是一个专业的天气助手，请友好地回复用户\n",
      "2. HumanMessage: 你好，我想查询今天的天气\n",
      "3. AIMessage: 好的，我来帮你查询天气信息\n",
      "4. ToolMessage: 北京今天晴转多云，温度 20-28°C\n",
      "\n",
      "演示 MessagesState 的状态管理：\n",
      "对话轮次: 4\n",
      "用户意图: weather_query\n",
      "工具输出: weather_data_retrieved\n"
     ]
    }
   ],
   "source": [
    "from langgraph.graph import MessagesState\n",
    "from langchain_core.messages import HumanMessage, AIMessage, ToolMessage, SystemMessage\n",
    "\n",
    "class MyChatState(MessagesState):\n",
    "    \"\"\"\n",
    "    自定义的 ChatState, 继承自 MessagesState, \n",
    "    自动包含 messages 状态键和 add_messages Reducer\n",
    "    \"\"\"\n",
    "    user_intent: str\n",
    "    tool_output: str\n",
    "    # ... 可以添加其他自定义的状态键 ...\n",
    "\n",
    "# 演示不同类型的消息\n",
    "print(\"演示不同类型的消息：\")\n",
    "\n",
    "# 创建不同类型的消息\n",
    "human_msg = HumanMessage(content=\"你好，我想查询今天的天气\")\n",
    "system_msg = SystemMessage(content=\"你是一个专业的天气助手，请友好地回复用户\")\n",
    "ai_msg = AIMessage(content=\"好的，我来帮你查询天气信息\")\n",
    "tool_msg = ToolMessage(content=\"北京今天晴转多云，温度 20-28°C\", tool_call_id=\"weather_001\")\n",
    "\n",
    "messages = [system_msg, human_msg, ai_msg, tool_msg]\n",
    "\n",
    "for i, msg in enumerate(messages, 1):\n",
    "    print(f\"{i}. {type(msg).__name__}: {msg.content}\")\n",
    "\n",
    "# 演示 MessagesState 的使用\n",
    "print(\"\\n演示 MessagesState 的状态管理：\")\n",
    "chat_state = {\n",
    "    \"messages\": messages,\n",
    "    \"user_intent\": \"weather_query\",\n",
    "    \"tool_output\": \"weather_data_retrieved\"\n",
    "}\n",
    "\n",
    "print(f\"对话轮次: {len(chat_state['messages'])}\")\n",
    "print(f\"用户意图: {chat_state['user_intent']}\")\n",
    "print(f\"工具输出: {chat_state['tool_output']}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "explanation-3-7",
   "metadata": {},
   "source": [
    "**💡 MessagesState 核心特性**：\n",
    "\n",
    "- **内置 `messages` 状态键**：自动提供消息列表管理\n",
    "- **默认 `add_messages` Reducer**：自动处理消息追加、更新和去重\n",
    "- **消息序列化与反序列化**：支持 JSON 兼容的字典格式传递消息数据\n",
    "- **可扩展性**：可以自由添加其他自定义状态键"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5d350119",
   "metadata": {},
   "source": [
    "##### 示例 3-8 & 3-9：在使用 MessagesState 的节点中使用 trim_messages 和 RemoveMessage"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "2e673c9d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "🏗️ 构建消息管理示例图...\n",
      "✅ 图构建完成！\n",
      "\n",
      "=== 🚀 消息状态管理示例 ===\n",
      "📋 初始消息历史:\n",
      "  1. [SystemMessage] 你是一个专业的AI助手，擅长回答各种问题。\n",
      "  2. [HumanMessage] 你好！很高兴见到你。\n",
      "  3. [AIMessage] 你好！我也很高兴为您服务。有什么可以帮助您的吗？\n",
      "  4. [HumanMessage] 这是一条很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长...\n",
      "  5. [AIMessage] 我明白了您的测试消息。\n",
      "  6. [HumanMessage] 再见！\n",
      "\n",
      "👤 添加用户消息节点\n",
      "➕ 添加消息: 我想了解人工智能的最新发展，特别是在自然语言处理方面的突破。\n",
      "\n",
      "🔧 过滤节点 (使用 RemoveMessage)\n",
      "📥 接收到 7 条消息\n",
      "🗑️ 标记移除寒暄消息: 你好！很高兴见到你。...\n",
      "🗑️ 标记移除寒暄消息: 你好！我也很高兴为您服务。有什么可以帮助您的吗？...\n",
      "🗑️ 标记移除寒暄消息: 再见！...\n",
      "📊 将移除 3 条消息\n",
      "🤖 LLM节点 (使用 trim_messages)\n",
      "📥 接收到 4 条消息\n",
      "  1. [SystemMessage] 你是一个专业的AI助手，擅长回答各种问题。\n",
      "  2. [HumanMessage] 这是一条很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长的测试消息，用来测试...\n",
      "  3. [AIMessage] 我明白了您的测试消息。\n",
      "  4. [HumanMessage] 我想了解人工智能的最新发展，特别是在自然语言处理方面的突破。\n",
      "✂️ 修剪后保留 4 条消息 (token限制: 200)\n",
      "💭 生成回复: \n",
      "\n",
      "以下是一些近期人工智能在自然语言处理（NLP）领域的重要突破和技术进展：\n",
      "\n",
      "---\n",
      "\n",
      "### **1. 大型语言模型（LLMs）的持续革新**\n",
      "- **参数量与计算效率**：模型规模进一步扩大（如GPT-4、PaLM 2等），但训练和推理成本显著降低，软硬件协同优化（如MoE架构）使得模型在资源受限的环境下仍能保持高性能。\n",
      "- **多语言能力**：模型支持更多语言（如低资源语言）的训练和推理，例如Meta的Llama系列、谷歌的Mistra等，推动了NLP在非英语场景的普及。\n",
      "- **任务泛化**：通过微调或提示工程，模型可以快速适应多种任务（如问答、翻译、代码生成），而无需额外的标注数据。\n",
      "\n",
      "---\n",
      "\n",
      "### **2. 多模态模型的突破**\n",
      "- **结合视觉与语言**：如CLIP、FLUX等模型能够同时处理图像和文本，实现跨模态理解与生成（例如图像描述生成、视觉问答）。\n",
      "- **文本到图像生成**：DALL·E 3、Midjourney等模型通过文本指令生成高质量图像，推动了AI在创作领域的应用。\n",
      "\n",
      "---\n",
      "\n",
      "### **3. 推理能力的提升**\n",
      "- **逻辑与数学推理**：模型在数学问题解决（如代数、微积分）和逻辑推理任务中表现显著增强，例如Google的Gemini Pro在代码生成和复杂推理任务中达到接近人类水平。\n",
      "- **持续学习与少样本学习**：模型通过少量数据即可学会新任务（如Few-Shot Learning），减少了对大规模数据集的依赖。\n",
      "\n",
      "---\n",
      "\n",
      "### **4. 因果推理与常识理解**\n",
      "- **因果模型**：研究者尝试构建因果关系推理框架，例如通过中间层动态生成因果图，帮助模型更好地理解事件之间的关联（如因果推理在医疗诊断中的应用）。\n",
      "- **增强常识知识**：通过引入外部知识库或细粒度对齐技术，提高模型对常识和隐含知识的理解能力（如模型在常识推理测试中的表现提升）。\n",
      "\n",
      "---\n",
      "\n",
      "### **5. 可解释性和伦理安全**\n",
      "- **可解释性工具**：开发了模型可视化工具（如LIME、SHAP）和因果推理框架，帮助用户理解模型决策过程。\n",
      "- **对抗攻击防御**：通过对抗训练和鲁棒优化技术，增强模型对恶意输入的防御能力（例如防御文本中的对抗样本攻击）。\n",
      "- **伦理与安全**：研究者致力于解决模型的偏见、幻觉等问题，例如通过多模态对齐和因果推理减少错误输出。\n",
      "\n",
      "---\n",
      "\n",
      "### **6. 实际应用场景的扩展**\n",
      "- **医疗领域**：NLP模型用于医学文献挖掘、疾病诊断辅助（如基于患者病史的聊天机器人）。\n",
      "- **金融领域**：模型在风险预测、自动报告生成和客户服务中广泛应用。\n",
      "- **法律领域**：法律文本分析、合同审查和案例预测成为研究热点。\n",
      "\n",
      "---\n",
      "\n",
      "### **7. 语言模型的工业落地**\n",
      "- **垂直领域定制**：针对特定行业（如客服、教育、娱乐）开发专用模型，利用领域知识提升性能。\n",
      "- **端到端解决方案**：企业通过模型部署实现自动化客服、智能写作等，减少人工干预。\n",
      "\n",
      "---\n",
      "\n",
      "如需了解某一领域或技术的细节，可以告诉我！\n",
      "\n",
      "=== ✨ 最终结果 ===\n",
      "📊 最终消息历史包含 5 条消息:\n",
      "  1. [SystemMessage] 你是一个专业的AI助手，擅长回答各种问题。\n",
      "  2. [HumanMessage] 这是一条很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长的测试消息，用来测试...\n",
      "  3. [AIMessage] 我明白了您的测试消息。\n",
      "  4. [HumanMessage] 我想了解人工智能的最新发展，特别是在自然语言处理方面的突破。\n",
      "  5. [AIMessage] \n",
      "\n",
      "以下是一些近期人工智能在自然语言处理（NLP）领域的重要突破和技术进展：\n",
      "\n",
      "---\n",
      "\n",
      "### *...\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.messages import HumanMessage, AIMessage, SystemMessage, trim_messages, RemoveMessage\n",
    "from langgraph.graph import MessagesState, StateGraph, START, END\n",
    "from langchain_openai import ChatOpenAI\n",
    "from langchain_core.messages.utils import count_tokens_approximately\n",
    "\n",
    "llm = ChatOpenAI(model=\"Qwen/Qwen3-8B\")\n",
    "\n",
    "# 使用 trim_messages 的节点\n",
    "def llm_node_with_trim(state: MessagesState):\n",
    "    print(\"🤖 LLM节点 (使用 trim_messages)\")\n",
    "    message_history = state['messages']\n",
    "    print(f\"📥 接收到 {len(message_history)} 条消息\")\n",
    "\n",
    "    # 显示原始消息\n",
    "    for i, msg in enumerate(message_history):\n",
    "        content_preview = msg.content[:50] + \"...\" if len(msg.content) > 50 else msg.content\n",
    "        print(f\"  {i+1}. [{msg.__class__.__name__}] {content_preview}\")\n",
    "\n",
    "    # 使用 trim_messages 修剪消息历史\n",
    "    trimmed_messages = trim_messages(\n",
    "        message_history,\n",
    "        max_tokens=200,  # 降低限制以便看到修剪效果\n",
    "        strategy=\"last\",\n",
    "        token_counter=count_tokens_approximately,\n",
    "        allow_partial=False\n",
    "    )\n",
    "\n",
    "    print(f\"✂️ 修剪后保留 {len(trimmed_messages)} 条消息 (token限制: 200)\")\n",
    "\n",
    "    # 生成回复\n",
    "    llm_response = llm.invoke(trimmed_messages)\n",
    "    print(f\"💭 生成回复: {llm_response.content}\")\n",
    "\n",
    "    return {\"messages\": [llm_response]}\n",
    "\n",
    "# 使用 filter_messages 的节点 (基于 RemoveMessage)\n",
    "def filter_node(state: MessagesState):\n",
    "    print(\"\\n🔧 过滤节点 (使用 RemoveMessage)\")\n",
    "    message_history = state['messages']\n",
    "    print(f\"📥 接收到 {len(message_history)} 条消息\")\n",
    "\n",
    "    remove_messages = []\n",
    "\n",
    "    # 过滤策略：移除包含\"你好\"或\"再见\"的寒暄消息\n",
    "    for msg in message_history:\n",
    "        if any(greeting in msg.content.lower() for greeting in [\"你好\",\n",
    "\"再见\", \"hello\", \"bye\"]):\n",
    "            print(f\"🗑️ 标记移除寒暄消息: {msg.content[:30]}...\")\n",
    "            remove_messages.append(RemoveMessage(id=msg.id))\n",
    "        # 移除过长的消息\n",
    "        elif len(msg.content) > 100:\n",
    "            print(f\"🗑️ 标记移除过长消息: {msg.content[:30]}...\")\n",
    "            remove_messages.append(RemoveMessage(id=msg.id))\n",
    "\n",
    "    if remove_messages:\n",
    "        print(f\"📊 将移除 {len(remove_messages)} 条消息\")\n",
    "        return {\"messages\": remove_messages}\n",
    "    else:\n",
    "        print(\"✅ 没有需要移除的消息\")\n",
    "        return {}\n",
    "\n",
    "# 添加用户消息的节点\n",
    "def add_user_message(state: MessagesState):\n",
    "    print(\"\\n👤 添加用户消息节点\")\n",
    "    new_message = HumanMessage(content=\"我想了解人工智能的最新发展，特别是在自然语言处理方面的突破。\")\n",
    "    print(f\"➕ 添加消息: {new_message.content}\")\n",
    "    return {\"messages\": [new_message]}\n",
    "\n",
    "# 创建图\n",
    "print(\"🏗️ 构建消息管理示例图...\")\n",
    "builder = StateGraph(MessagesState)\n",
    "\n",
    "# 添加节点\n",
    "builder.add_node(\"add_message\", add_user_message)\n",
    "builder.add_node(\"filter\", filter_node)\n",
    "builder.add_node(\"llm_trim\", llm_node_with_trim)\n",
    "\n",
    "# 定义边\n",
    "builder.add_edge(START, \"add_message\")\n",
    "builder.add_edge(\"add_message\", \"filter\")\n",
    "builder.add_edge(\"filter\", \"llm_trim\")\n",
    "builder.add_edge(\"llm_trim\", END)\n",
    "\n",
    "# 编译图\n",
    "graph = builder.compile()\n",
    "print(\"✅ 图构建完成！\")\n",
    "\n",
    "# 准备初始消息历史\n",
    "print(\"\\n=== 🚀 消息状态管理示例 ===\")\n",
    "\n",
    "initial_messages = [\n",
    "    SystemMessage(content=\"你是一个专业的AI助手，擅长回答各种问题。\"),\n",
    "    HumanMessage(content=\"你好！很高兴见到你。\"),\n",
    "    AIMessage(content=\"你好！我也很高兴为您服务。有什么可以帮助您的吗？\"),\n",
    "    HumanMessage(content=\"这是一条很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长的测试消息，用来测试过滤功能。\"),\n",
    "    AIMessage(content=\"我明白了您的测试消息。\"),\n",
    "    HumanMessage(content=\"再见！\"),\n",
    "]\n",
    "\n",
    "print(\"📋 初始消息历史:\")\n",
    "for i, msg in enumerate(initial_messages):\n",
    "    content_preview = msg.content[:40] + \"...\" if len(msg.content) > 40 else msg.content\n",
    "    print(f\"  {i+1}. [{msg.__class__.__name__}] {content_preview}\")\n",
    "\n",
    "# 运行图\n",
    "result = graph.invoke({\"messages\": initial_messages})\n",
    "\n",
    "print(f\"\\n=== ✨ 最终结果 ===\")\n",
    "print(f\"📊 最终消息历史包含 {len(result['messages'])} 条消息:\")\n",
    "for i, msg in enumerate(result['messages']):\n",
    "    content_preview = msg.content[:50] + \"...\" if len(msg.content) > 50 else msg.content\n",
    "    print(f\"  {i+1}. [{msg.__class__.__name__}] {content_preview}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2e029ef6",
   "metadata": {},
   "source": [
    "1. `trim_messages` 功能：\n",
    "- 根据 Token 限制自动修剪消息历史\n",
    "- 使用 `\"last\"` 策略保留最近的消息\n",
    "- 模拟 Token 计数器进行 Token 估算\n",
    "2. `filter_messages` (`RemoveMessage`) 功能：\n",
    "- 根据内容规则过滤消息（移除寒暄语）\n",
    "- 根据长度规则过滤消息（移除过长消息）\n",
    "- 使用 `RemoveMessage` 标记要删除的消息\n",
    "3. `MessagesState` 自动管理：\n",
    "- 自动使用 `add_messages` Reducer\n",
    "- 支持消息的添加和移除操作\n",
    "- 维护完整的消息历史\n",
    "4. 实际工作流程：\n",
    "- 添加新的用户消息\n",
    "- 过滤不需要的消息\n",
    "- 使用修剪后的消息生成 LLM 回复\n",
    "- 展示完整的消息管理流程"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "section-3-1-2",
   "metadata": {},
   "source": [
    "### 3.1.2 节点（Node）\n",
    "\n",
    "节点是 LangGraph 图结构中的基本计算单元。每一个节点都封装了一个独立的计算逻辑，例如调用语言模型、执行工具、进行条件判断、或者仅仅是一个简单的数据处理函数。\n",
    "\n",
    "在 LangGraph 中，节点本质上就是一个 Python 函数。这个函数接收当前的状态作为输入，并返回一个新的状态（或者状态的更新部分）作为输出。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "example-3-10",
   "metadata": {},
   "source": [
    "##### 示例 3-10：节点函数的基本结构"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "cell-5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "测试节点函数：\n",
      "输入状态: {'some_key': 'hello world', 'existing_key': 'existing_value'}\n",
      "节点输出: {'some_key': '处理后的数据: HELLO WORLD', 'another_key': 'new_value'}\n",
      "更新后状态: {'some_key': '处理后的数据: HELLO WORLD', 'existing_key': 'existing_value', 'another_key': 'new_value'}\n"
     ]
    }
   ],
   "source": [
    "def my_node(state):\n",
    "    \"\"\"\n",
    "    节点函数示例\n",
    "    \"\"\"\n",
    "    # 从状态中读取数据\n",
    "    input_data = state.get(\"some_key\", \"default_value\")\n",
    "    \n",
    "    # 执行节点计算逻辑\n",
    "    def process_data(data):\n",
    "        return f\"处理后的数据: {data.upper()}\"\n",
    "    \n",
    "    output_data = process_data(input_data)\n",
    "    \n",
    "    # 返回新的状态（或状态的更新部分）\n",
    "    return {\"some_key\": output_data, \"another_key\": \"new_value\"}\n",
    "\n",
    "# 测试节点函数\n",
    "print(\"测试节点函数：\")\n",
    "test_state = {\"some_key\": \"hello world\", \"existing_key\": \"existing_value\"}\n",
    "print(f\"输入状态: {test_state}\")\n",
    "\n",
    "result = my_node(test_state)\n",
    "print(f\"节点输出: {result}\")\n",
    "\n",
    "# 模拟状态更新（LangGraph会自动处理状态合并）\n",
    "updated_state = {**test_state, **result}\n",
    "print(f\"更新后状态: {updated_state}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "example-3-11",
   "metadata": {},
   "source": [
    "##### 示例 3-11：一个包含 LLM 节点的 LangGraph 图"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "cell-6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "LangGraph 图构建完成\n",
      "节点: llm_node\n",
      "边: START -> llm_node -> END\n",
      "\n",
      "测试图执行：\n",
      "执行结果: {'messages': [], 'user_question': '你好，LangGraph！', 'llm_response': '\\n\\n你好！我是通义千问，很高兴认识你。LangGraph 是 LangChain 的一个扩展库，它提供了一种通过图结构来组织和操作数据的方式，使得构建更复杂的 AI 应用（如知识图谱、问答系统、推荐系统等）变得更加简单和高效。如果你有任何关于 LangGraph 的问题，或者需要帮助构建某个具体的应用，欢迎随时告诉我！'}\n"
     ]
    }
   ],
   "source": [
    "from langgraph.graph import StateGraph, START, END, MessagesState\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "# 定义状态结构体 \n",
    "class ChatState(MessagesState):\n",
    "    user_question: str  # 用户问题\n",
    "    llm_response: str   # LLM回复\n",
    "\n",
    "# 定义 LLM 节点 \n",
    "def llm_node(state):\n",
    "    prompt = ChatPromptTemplate.from_messages([\n",
    "        (\"human\", \"{question}\")\n",
    "    ])\n",
    "    model = ChatOpenAI(model=\"Qwen/Qwen3-8B\")\n",
    "    chain = prompt | model\n",
    "    \n",
    "    response = chain.invoke({\"question\": state['user_question']}).content\n",
    "    return {\"llm_response\": response}\n",
    "\n",
    "# 构建图 \n",
    "builder = StateGraph(ChatState)\n",
    "builder.add_node(\"llm_node\", llm_node)\n",
    "builder.add_edge(START, \"llm_node\")\n",
    "builder.add_edge(\"llm_node\", END)\n",
    "graph = builder.compile()\n",
    "\n",
    "print(\"LangGraph 图构建完成\")\n",
    "print(\"节点: llm_node\")\n",
    "print(\"边: START -> llm_node -> END\")\n",
    "\n",
    "# 测试图的执行\n",
    "print(\"\\n测试图执行：\")\n",
    "try:\n",
    "    result = graph.invoke({\"user_question\": \"你好，LangGraph！\"})\n",
    "    print(f\"执行结果: {result}\")\n",
    "except Exception as e:\n",
    "    print(f\"需要配置API密钥才能实际运行LLM: {e}\")\n",
    "    print(\"图结构已成功创建，可以在配置API后运行\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "75e7a7e5",
   "metadata": {},
   "source": [
    "##### 示例 3-12：为 LangGraph 节点配置重试策略的代码示例"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "d99a1f2f",
   "metadata": {},
   "outputs": [
    {
     "ename": "ImportError",
     "evalue": "cannot import name 'RetryPolicy' from 'langgraph.pregel' (/Users/haili/workspaces/langgraph-in-action/.venv/lib/python3.13/site-packages/langgraph/pregel/__init__.py)",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mImportError\u001b[39m                               Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[11]\u001b[39m\u001b[32m, line 10\u001b[39m\n\u001b[32m      8\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mlangchain_core\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mmessages\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m AIMessage, BaseMessage, HumanMessage\n\u001b[32m      9\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mlanggraph\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mgraph\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m StateGraph, START, END\n\u001b[32m---> \u001b[39m\u001b[32m10\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mlanggraph\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mpregel\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m RetryPolicy\n\u001b[32m     12\u001b[39m \u001b[38;5;66;03m# 模拟数据库类\u001b[39;00m\n\u001b[32m     13\u001b[39m \u001b[38;5;28;01mclass\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mMockSQLDatabase\u001b[39;00m:\n",
      "\u001b[31mImportError\u001b[39m: cannot import name 'RetryPolicy' from 'langgraph.pregel' (/Users/haili/workspaces/langgraph-in-action/.venv/lib/python3.13/site-packages/langgraph/pregel/__init__.py)"
     ]
    }
   ],
   "source": [
    "import operator\n",
    "import sqlite3\n",
    "import random\n",
    "import time\n",
    "from typing import Annotated, Sequence\n",
    "from typing_extensions import TypedDict\n",
    "\n",
    "from langchain_core.messages import AIMessage, BaseMessage, HumanMessage\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "from langgraph.pregel import RetryPolicy\n",
    "\n",
    "# 模拟数据库类\n",
    "class MockSQLDatabase:\n",
    "    def __init__(self):\n",
    "        self.connection_stable = False\n",
    "        self.call_count = 0\n",
    "\n",
    "    def run(self, query):\n",
    "        self.call_count += 1\n",
    "        print(f\"🗄️ 数据库查询 (第{self.call_count}次): {query}\")\n",
    "\n",
    "        # 模拟不稳定的数据库连接 - 前2次调用会失败\n",
    "        if self.call_count <= 2:\n",
    "            print(f\"❌ 数据库连接失败 (模拟错误)\")\n",
    "            raise sqlite3.OperationalError(\"数据库连接超时\")\n",
    "\n",
    "        print(f\"✅ 数据库查询成功\")\n",
    "        return \"艺术家数据: Van Gogh, Picasso, Da Vinci, Monet, Renoir\"\n",
    "\n",
    "# 模拟 LLM 类\n",
    "class MockChatOpenAI:\n",
    "    def __init__(self, model=\"mock-model\"):\n",
    "        self.model = model\n",
    "        self.call_count = 0\n",
    "\n",
    "    def invoke(self, messages):\n",
    "        self.call_count += 1\n",
    "        print(f\"🤖 LLM调用 (第{self.call_count}次)\")\n",
    "\n",
    "        # 模拟 LLM 偶尔失败 - 30% 概率失败\n",
    "        if random.random() < 0.3:\n",
    "            print(f\"❌ LLM服务暂时不可用 (模拟错误)\")\n",
    "            raise ConnectionError(\"LLM服务连接失败\")\n",
    "\n",
    "        last_message = messages[-1] if messages else None\n",
    "        content = f\"基于查询结果，我为您找到了相关的艺术家信息。这是第{self.call_count}次成功调用的响应。\"\n",
    "        print(f\"✅ LLM响应生成成功\")\n",
    "        return AIMessage(content=content)\n",
    "\n",
    "# 初始化模拟组件\n",
    "db = MockSQLDatabase()\n",
    "model = MockChatOpenAI(model=\"Mock-GPT-4\")\n",
    "\n",
    "# 定义图的状态\n",
    "class AgentState(TypedDict):\n",
    "    messages: Annotated[Sequence[BaseMessage], operator.add]\n",
    "\n",
    "def query_database(state):\n",
    "    \"\"\"查询数据库节点 - 配置了特定异常重试\"\"\"\n",
    "    print(f\"\\n📊 执行数据库查询节点...\")\n",
    "    query_result = db.run(\"SELECT * FROM Artist LIMIT 10;\")\n",
    "    return {\"messages\": [AIMessage(content=f\"数据库查询结果: {query_result}\")]}\n",
    "\n",
    "def call_model(state):\n",
    "    \"\"\"调用模型节点 - 配置了最大重试次数\"\"\"\n",
    "    print(f\"\\n🧠 执行模型调用节点...\")\n",
    "    response = model.invoke(state[\"messages\"])\n",
    "    return {\"messages\": [response]}\n",
    "\n",
    "def user_input_node(state):\n",
    "    \"\"\"用户输入节点\"\"\"\n",
    "    print(f\"\\n👤 添加用户输入...\")\n",
    "    user_message = HumanMessage(content=\"请帮我查询一些著名艺术家的信息\")\n",
    "    print(f\"📝 用户问题: {user_message.content}\")\n",
    "    return {\"messages\": [user_message]}\n",
    "\n",
    "# 定义图 builder\n",
    "print(\"🏗️ 构建带重试策略的 LangGraph...\")\n",
    "builder = StateGraph(AgentState)\n",
    "\n",
    "# 添加用户输入节点\n",
    "builder.add_node(\"user_input\", user_input_node)\n",
    "\n",
    "# 为 call_model 节点配置重试策略: 最大重试 5 次，包含退避策略\n",
    "builder.add_node(\n",
    "    \"model\",\n",
    "    call_model,\n",
    "    retry=RetryPolicy(\n",
    "        max_attempts=5,           # 最大重试5次\n",
    "        initial_interval=0.5,     # 初始重试间隔0.5秒\n",
    "        backoff_factor=2.0,       # 退避因子2.0 (指数退避)\n",
    "        max_interval=8.0,         # 最大重试间隔8秒\n",
    "        jitter=True              # 添加随机抖动\n",
    "    )\n",
    ")\n",
    "\n",
    "# 为 query_database 节点配置重试策略: 针对 sqlite3.OperationalError 异常进行重试\n",
    "builder.add_node(\n",
    "    \"query_database\",\n",
    "    query_database,\n",
    "    retry=RetryPolicy(\n",
    "        retry_on=sqlite3.OperationalError,  # 只对数据库操作错误重试\n",
    "        max_attempts=4,                     # 最大重试4次\n",
    "        initial_interval=1.0,               # 初始间隔1秒\n",
    "        backoff_factor=1.5                  # 较小的退避因子\n",
    "    )\n",
    ")\n",
    "\n",
    "# 定义边\n",
    "builder.add_edge(START, \"user_input\")\n",
    "builder.add_edge(\"user_input\", \"model\")\n",
    "builder.add_edge(\"model\", \"query_database\")\n",
    "builder.add_edge(\"query_database\", END)\n",
    "\n",
    "# 编译图\n",
    "graph = builder.compile()\n",
    "print(\"✅ 图构建完成！\")\n",
    "\n",
    "# 测试运行\n",
    "print(\"\\n=== 🚀 重试策略演示 ===\")\n",
    "print(\"📋 测试场景:\")\n",
    "print(\"  - 数据库节点: 前2次调用会失败，第3次成功\")\n",
    "print(\"  - 模型节点: 30% 概率失败，会自动重试\")\n",
    "print(\"  - 两个节点都配置了不同的重试策略\\n\")\n",
    "\n",
    "try:\n",
    "    # 运行图\n",
    "    result = graph.invoke({\"messages\": []})\n",
    "\n",
    "    print(f\"\\n=== ✨ 执行完成 ===\")\n",
    "    print(f\"📊 最终消息数量: {len(result['messages'])}\")\n",
    "    for i, msg in enumerate(result['messages']):\n",
    "        print(f\"  {i+1}. [{msg.__class__.__name__}] {msg.content[:60]}...\")\n",
    "\n",
    "    print(f\"\\n=== 📈 重试统计 ===\")\n",
    "    print(f\"🗄️ 数据库调用次数: {db.call_count}\")\n",
    "    print(f\"🤖 模型调用次数: {model.call_count}\")\n",
    "\n",
    "except Exception as e:\n",
    "    print(f\"\\n❌ 执行失败: {e}\")\n",
    "    print(f\"🗄️ 数据库调用次数: {db.call_count}\")\n",
    "    print(f\"🤖 模型调用次数: {model.call_count}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "533b4744",
   "metadata": {},
   "source": [
    "1. `RetryPolicy` 配置：\n",
    "- `model` 节点：通用重试策略，处理各种异常\n",
    "- `query_database` 节点：针对特定数据库异常的重试策略\n",
    "2. 不同的重试参数：\n",
    "- 最大重试次数、初始间隔、退避因子等\n",
    "- 展示指数退避和抖动机制\n",
    "3. 模拟失败场景：\n",
    "- 数据库连接不稳定（前几次必然失败）\n",
    "- LLM 服务偶尔不可用（随机失败）\n",
    "4. 重试效果演示：\n",
    "- 显示每次重试的过程\n",
    "- 统计实际调用次数\n",
    "- 展示重试策略的实际效果"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "section-3-1-3",
   "metadata": {},
   "source": [
    "### 3.1.3 边（Edge）\n",
    "\n",
    "边在 LangGraph 中负责连接不同的节点，定义智能体系统的执行流程。边决定了在执行完一个节点之后，下一步应该执行哪个节点。\n",
    "\n",
    "LangGraph 主要支持两种类型的边：\n",
    "- **普通边**：定义节点间固定的、无条件的连接关系\n",
    "- **条件边**：提供基于状态动态路由的能力"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "example-3-13",
   "metadata": {},
   "source": [
    "##### 示例 3-13：意图识别与技能路由流程"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "意图识别与技能路由图构建完成\n",
      "\n",
      "用户输入: 今天天气怎么样？\n",
      "识别意图: 查询天气\n",
      "系统回复: 今天天气晴朗，温度 22-28°C\n",
      "\n",
      "用户输入: 我要预订明天到北京的机票\n",
      "识别意图: 预订机票\n",
      "系统回复: 为您查找合适的机票选项...\n",
      "\n",
      "用户输入: 我要投诉你们的服务\n",
      "识别意图: 投诉建议\n",
      "系统回复: 感谢您的反馈，我们会认真处理您的建议\n",
      "\n",
      "用户输入: 你好\n",
      "识别意图: 未知\n",
      "系统回复: 无回复\n"
     ]
    }
   ],
   "source": [
    "from langgraph.graph import StateGraph, START, END\n",
    "from typing_extensions import TypedDict\n",
    "\n",
    "# 定义状态\n",
    "class IntentState(TypedDict):\n",
    "    user_input: str\n",
    "    user_intent: str\n",
    "    response: str\n",
    "\n",
    "# 意图识别节点\n",
    "def intent_recognition_node(state):\n",
    "    user_input = state['user_input'].lower()\n",
    "    \n",
    "    # 简单的意图识别逻辑\n",
    "    if '天气' in user_input or 'weather' in user_input:\n",
    "        intent = \"查询天气\"\n",
    "    elif '机票' in user_input or 'flight' in user_input:\n",
    "        intent = \"预订机票\"\n",
    "    elif '投诉' in user_input or 'complaint' in user_input:\n",
    "        intent = \"投诉建议\"\n",
    "    else:\n",
    "        intent = \"未知\"\n",
    "    \n",
    "    return {\"user_intent\": intent}\n",
    "\n",
    "# 技能节点\n",
    "def weather_query_node(state):\n",
    "    return {\"response\": \"今天天气晴朗，温度 22-28°C\"}\n",
    "\n",
    "def flight_booking_node(state):\n",
    "    return {\"response\": \"为您查找合适的机票选项...\"}\n",
    "\n",
    "def complaint_suggestion_node(state):\n",
    "    return {\"response\": \"感谢您的反馈，我们会认真处理您的建议\"}\n",
    "\n",
    "# 条件路由函数\n",
    "def route_to_skill(state):\n",
    "    \"\"\"条件函数，根据用户意图路由到不同的技能节点\"\"\"\n",
    "    user_intent = state['user_intent']\n",
    "    if user_intent == \"查询天气\":\n",
    "        return \"weather_query_node\"  # 跳转到查询天气节点\n",
    "    elif user_intent == \"预订机票\":\n",
    "        return \"flight_booking_node\"  # 跳转到预订机票节点\n",
    "    elif user_intent == \"投诉建议\":\n",
    "        return \"complaint_suggestion_node\"  # 跳转到投诉建议处理节点\n",
    "    else:\n",
    "        return END  # 无法识别意图，结束流程\n",
    "\n",
    "# 构建图\n",
    "builder = StateGraph(IntentState)\n",
    "\n",
    "# 添加节点\n",
    "builder.add_node(\"intent_recognition_node\", intent_recognition_node)\n",
    "builder.add_node(\"weather_query_node\", weather_query_node)\n",
    "builder.add_node(\"flight_booking_node\", flight_booking_node)\n",
    "builder.add_node(\"complaint_suggestion_node\", complaint_suggestion_node)\n",
    "\n",
    "# 添加边\n",
    "builder.add_edge(START, \"intent_recognition_node\")\n",
    "builder.add_conditional_edges(\n",
    "    \"intent_recognition_node\", \n",
    "    route_to_skill,\n",
    "    [\"weather_query_node\", \"flight_booking_node\", \"complaint_suggestion_node\", END]\n",
    ")\n",
    "builder.add_edge(\"weather_query_node\", END)\n",
    "builder.add_edge(\"flight_booking_node\", END)\n",
    "builder.add_edge(\"complaint_suggestion_node\", END)\n",
    "\n",
    "# 编译图\n",
    "intent_graph = builder.compile()\n",
    "\n",
    "print(\"意图识别与技能路由图构建完成\")\n",
    "\n",
    "# 测试不同的用户输入\n",
    "test_inputs = [\n",
    "    \"今天天气怎么样？\",\n",
    "    \"我要预订明天到北京的机票\",\n",
    "    \"我要投诉你们的服务\",\n",
    "    \"你好\"\n",
    "]\n",
    "\n",
    "for user_input in test_inputs:\n",
    "    print(f\"\\n用户输入: {user_input}\")\n",
    "    result = intent_graph.invoke({\"user_input\": user_input})\n",
    "    print(f\"识别意图: {result.get('user_intent', '未识别')}\")\n",
    "    print(f\"系统回复: {result.get('response', '无回复')}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "explanation-3-13",
   "metadata": {},
   "source": [
    "**💡 边的类型和应用场景**：\n",
    "\n",
    "- **条件边**：`add_conditional_edges()` 实现动态路由，根据运行时状态选择路径\n",
    "- **普通边**：`add_edge()` 创建固定连接，适用于确定的流程序列\n",
    "- **应用场景**：意图识别、工具选择、错误处理、流程分支等"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "section-3-1-4",
   "metadata": {},
   "source": [
    "### 3.1.4 命令（Command）\n",
    "\n",
    "命令（Command）是 LangGraph 新推出的强大工具，它允许我们将状态更新和流程控制逻辑整合到同一个节点中。Command 打破了节点和边功能上的传统分工，赋予了节点更强大的流程控制能力。\n",
    "\n",
    "一个 `Command` 对象主要包含以下两个部分：\n",
    "- `update`（状态更新）：指定需要更新的状态键值对\n",
    "- `goto`（流程跳转）：指定下一步要执行的节点名称"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "example-3-15",
   "metadata": {},
   "source": [
    "##### 示例 3-15：使用 Command 的节点函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Command 示例图构建完成\n",
      "\n",
      "输入数据: 请执行 process_a 操作\n",
      "决策结果: 决定跳转到: node_a\n",
      "处理结果: 已处理: 请执行 process_a 操作 -> 经过节点A处理\n",
      "\n",
      "输入数据: 需要 process_b 处理\n",
      "决策结果: 决定跳转到: node_b\n",
      "处理结果: 已处理: 需要 process_b 处理 -> 经过节点B处理\n",
      "\n",
      "输入数据: 其他类型的操作\n",
      "决策结果: 决定跳转到: node_c\n",
      "处理结果: 已处理: 其他类型的操作 -> 经过节点C处理\n"
     ]
    }
   ],
   "source": [
    "from langgraph.types import Command\n",
    "from typing import Literal\n",
    "from typing_extensions import TypedDict\n",
    "\n",
    "# 定义状态\n",
    "class CommandState(TypedDict):\n",
    "    input_data: str\n",
    "    processed_data: str\n",
    "    decision_result: str\n",
    "\n",
    "def decide_next_node(state):\n",
    "    \"\"\"根据状态决定下一个节点\"\"\"\n",
    "    input_data = state['input_data'].lower()\n",
    "    if 'process_a' in input_data:\n",
    "        return 'node_a'\n",
    "    elif 'process_b' in input_data:\n",
    "        return 'node_b'\n",
    "    else:\n",
    "        return 'node_c'\n",
    "\n",
    "def decision_node(state) -> Command[Literal[\"node_a\", \"node_b\", \"node_c\"]]:\n",
    "    \"\"\"\n",
    "    使用 Command 的节点函数示例\n",
    "    \"\"\"\n",
    "    # 处理输入数据\n",
    "    input_data = state['input_data']\n",
    "    processed_result = f\"已处理: {input_data}\"\n",
    "    \n",
    "    # 决定下一个节点\n",
    "    next_node_name = decide_next_node(state)\n",
    "    \n",
    "    return Command(\n",
    "        update={\"processed_data\": processed_result, \"decision_result\": f\"决定跳转到: {next_node_name}\"},  # 状态更新\n",
    "        goto=next_node_name  # 流程跳转指令\n",
    "    )\n",
    "\n",
    "# 目标节点\n",
    "def node_a(state):\n",
    "    return {\"processed_data\": state['processed_data'] + \" -> 经过节点A处理\"}\n",
    "\n",
    "def node_b(state):\n",
    "    return {\"processed_data\": state['processed_data'] + \" -> 经过节点B处理\"}\n",
    "\n",
    "def node_c(state):\n",
    "    return {\"processed_data\": state['processed_data'] + \" -> 经过节点C处理\"}\n",
    "\n",
    "# 构建使用 Command 的图\n",
    "builder = StateGraph(CommandState)\n",
    "\n",
    "# 添加节点\n",
    "builder.add_node(\"decision_node\", decision_node)\n",
    "builder.add_node(\"node_a\", node_a)\n",
    "builder.add_node(\"node_b\", node_b)\n",
    "builder.add_node(\"node_c\", node_c)\n",
    "\n",
    "# 添加边\n",
    "builder.add_edge(START, \"decision_node\")\n",
    "builder.add_edge(\"node_a\", END)\n",
    "builder.add_edge(\"node_b\", END)\n",
    "builder.add_edge(\"node_c\", END)\n",
    "\n",
    "# 编译图\n",
    "command_graph = builder.compile()\n",
    "\n",
    "print(\"Command 示例图构建完成\")\n",
    "\n",
    "# 测试不同的输入\n",
    "test_inputs = [\n",
    "    \"请执行 process_a 操作\",\n",
    "    \"需要 process_b 处理\",\n",
    "    \"其他类型的操作\"\n",
    "]\n",
    "\n",
    "for input_data in test_inputs:\n",
    "    print(f\"\\n输入数据: {input_data}\")\n",
    "    result = command_graph.invoke({\"input_data\": input_data})\n",
    "    print(f\"决策结果: {result.get('decision_result', '无')}\")\n",
    "    print(f\"处理结果: {result.get('processed_data', '无')}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "explanation-3-15",
   "metadata": {},
   "source": [
    "**💡 Command 的核心价值**：\n",
    "\n",
    "- **内聚性**：将状态更新和流程控制逻辑封装在同一个节点中\n",
    "- **类型安全**：通过 `Literal` 类型提示确保跳转目标的正确性\n",
    "- **可视化支持**：LangGraph 可以静态分析 Command 的类型提示生成准确的流程图\n",
    "- **灵活性**：特别适合多智能体协作中的\"交接\"场景"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4rtx30sakwu",
   "metadata": {},
   "source": [
    "## 3.2 流程控制：分支与并发\n",
    "\n",
    "在上一节中，我们了解了 LangGraph 的核心原语：状态、节点、边和命令。有了这些原语，我们就可以构建简单的线性流程。但在实际的智能体系统中，流程往往不是简单的线性执行，而是会根据不同的情况和条件，出现分支、并行、循环等复杂的控制流。\n",
    "\n",
    "本节我们将重点探讨 LangGraph 中的流程控制机制，特别是如何利用 LangGraph 实现并行分支 (Parallel Branching)，构建能够并发执行多个任务、提高系统效率的智能体系统。\n",
    "\n",
    "在最简单的 LangGraph 流程中，我们使用普通边将节点串联起来，形成一条线性的执行路径。但线性流程有其局限性：\n",
    "\n",
    "- **无法处理条件判断**：线性流程无法根据状态或外部条件，动态地选择不同的执行路径\n",
    "- **无法实现并行执行**：线性流程只能串行地执行节点，无法同时执行多个独立的任务\n",
    "- **效率较低**：对于一些可以并行处理的任务，线性流程的串行执行方式会浪费计算资源\n",
    "\n",
    "为了克服这些局限性，LangGraph 提供了强大的流程控制机制，其中分支 (Branching) 和 并发 (Concurrency) 是核心要素。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "w6cgoebnpwf",
   "metadata": {},
   "source": [
    "### 3.2.1 并行分支：扇出 (Fan-out) 与扇入 (Fan-in)\n",
    "\n",
    "LangGraph 实现并行分支的核心机制是扇出 (Fan-out) 和 扇入 (Fan-in)。\n",
    "\n",
    "- **扇出 (Fan-out)**：从一个节点出发，同时触发多个下游节点，使得流程并行地向多个方向分支\n",
    "- **扇入 (Fan-in)**：将多个并行分支的流程汇聚到一个共同的下游节点，实现并行流程的同步和汇合\n",
    "\n",
    "要实现扇出，最简单的方式是为一个节点添加多个出边，将该节点同时连接到多个下游节点。当 LangGraph 执行到该节点时，会同时、并发地触发所有出边指向的下游节点。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3jfe3zy3a42",
   "metadata": {},
   "source": [
    "##### 示例 3-16：扇出和扇入的完整并行分支流程"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "x5qfd76xu6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "并行分支图构建完成\n",
      "流程: START -> node_a -> (node_b, node_c 并行) -> node_d -> END\n",
      "\n",
      "执行并行分支流程:\n",
      "Adding \"A\" to []\n",
      "Adding \"B\" to ['A']\n",
      "Adding \"C\" to ['A']\n",
      "Adding \"D\" to ['A', 'B', 'C']\n",
      "\n",
      "最终结果: {'aggregate': ['A', 'B', 'C', 'D']}\n"
     ]
    }
   ],
   "source": [
    "import operator\n",
    "from typing import Annotated, Any\n",
    "from typing_extensions import TypedDict\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "\n",
    "# 定义状态，使用 operator.add Reducer 处理并行分支的状态更新\n",
    "class ParallelState(TypedDict):\n",
    "    aggregate: Annotated[list, operator.add]\n",
    "\n",
    "def node_a(state: ParallelState):\n",
    "    print(f'Adding \"A\" to {state[\"aggregate\"]}')\n",
    "    return {\"aggregate\": [\"A\"]}\n",
    "\n",
    "def node_b(state: ParallelState):\n",
    "    print(f'Adding \"B\" to {state[\"aggregate\"]}')\n",
    "    return {\"aggregate\": [\"B\"]}\n",
    "\n",
    "def node_c(state: ParallelState):\n",
    "    print(f'Adding \"C\" to {state[\"aggregate\"]}')\n",
    "    return {\"aggregate\": [\"C\"]}\n",
    "\n",
    "def node_d(state: ParallelState):\n",
    "    print(f'Adding \"D\" to {state[\"aggregate\"]}')\n",
    "    return {\"aggregate\": [\"D\"]}\n",
    "\n",
    "# 构建并行分支图\n",
    "builder = StateGraph(ParallelState)\n",
    "\n",
    "# 添加节点\n",
    "builder.add_node(\"node_a\", node_a)\n",
    "builder.add_node(\"node_b\", node_b)\n",
    "builder.add_node(\"node_c\", node_c)\n",
    "builder.add_node(\"node_d\", node_d)\n",
    "\n",
    "# 添加边：实现扇出和扇入\n",
    "builder.add_edge(START, \"node_a\")  # 从 START 到 node_a\n",
    "builder.add_edge(\"node_a\", \"node_b\")  # node_a 扇出到 node_b\n",
    "builder.add_edge(\"node_a\", \"node_c\")  # node_a 扇出到 node_c (实现扇出)\n",
    "builder.add_edge(\"node_b\", \"node_d\")  # node_b 扇入到 node_d\n",
    "builder.add_edge(\"node_c\", \"node_d\")  # node_c 扇入到 node_d (实现扇入)\n",
    "builder.add_edge(\"node_d\", END)  # node_d 到 END\n",
    "\n",
    "# 编译图\n",
    "parallel_graph = builder.compile()\n",
    "\n",
    "print(\"并行分支图构建完成\")\n",
    "print(\"流程: START -> node_a -> (node_b, node_c 并行) -> node_d -> END\")\n",
    "\n",
    "# 执行并行分支流程\n",
    "print(\"\\n执行并行分支流程:\")\n",
    "result = parallel_graph.invoke({\"aggregate\": []})\n",
    "print(f\"\\n最终结果: {result}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "kbidijuub19",
   "metadata": {},
   "source": [
    "**💡 并行分支核心概念解析**：\n",
    "\n",
    "- **扇出效果**：节点 A 执行完后，同时触发节点 B 和 C，两个节点并发执行\n",
    "- **扇入同步**：节点 D 必须等待节点 B 和 C 都执行完成后才开始执行，起到同步点作用\n",
    "- **状态 Reducer**：使用 `operator.add` Reducer 确保并行分支的状态更新能正确合并\n",
    "- **执行效率**：并行执行比串行执行节省时间，如果 B 需要 3 秒，C 需要 5 秒，并行执行总共只需 5 秒\n",
    "\n",
    "可以看到执行结果中，节点 B 和 C 是并发执行的，然后节点 D 汇聚了两个分支的结果。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ew455k5fbao",
   "metadata": {},
   "source": [
    "### 3.2.2 并发 (Concurrency) 而非并行 (Parallelism)\n",
    "\n",
    "需要明确的是，LangGraph 实现的并非真正的并行 (Parallelism)，而是并发 (Concurrency)。理解这个区别对于正确使用 LangGraph 非常重要：\n",
    "\n",
    "#### 并发 vs 并行的本质区别\n",
    "\n",
    "- **并行 (Parallelism)**：同时执行多个独立的任务，需要多个物理计算资源（多核 CPU、多台机器）真正地同时运行不同的代码\n",
    "- **并发 (Concurrency)**：在单计算资源上，\"看似同时\"执行多个任务，通过时间片轮转或异步 IO，CPU 在多个任务之间快速切换\n",
    "\n",
    "#### LangGraph 的 Superstep 执行模型\n",
    "\n",
    "LangGraph 借鉴了 Apache Pregel 分布式图计算框架的 Superstep (超步) 概念：\n",
    "\n",
    "- **Superstep**：图执行的基本迭代单元，代表 LangGraph 图执行过程中的一个\"步骤\"\n",
    "- **并发执行**：在每个 Superstep 中，LangGraph 会尽可能地并发执行所有\"就绪\"的节点\n",
    "- **同步屏障**：当 Superstep 内的所有节点都执行完成后，进行全局同步，应用状态更新，进入下一个 Superstep"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cre0fjl04um",
   "metadata": {},
   "source": [
    "##### 示例 3-17 & 3-18：Superstep 执行模式演示"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "495pf8a7d68",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Superstep 执行模式演示:\n",
      "Superstep 1: 执行 A\n",
      "Superstep 2: 并发执行 B 和 C (等待同步)\n",
      "Superstep 3: 同步后执行 D\n",
      "\n",
      "Superstep 1: 执行节点 A\n",
      "Superstep 2: 执行节点 B (并发)\n",
      "Superstep 2: 执行节点 C (并发)\n",
      "Superstep 3: 执行节点 D (同步后)\n",
      "\\n执行完成，总时间: 0.422 秒\n",
      "执行日志:\n",
      "  - Superstep 1: Node A executed\n",
      "  - Superstep 2: Node B executed\n",
      "  - Superstep 2: Node C executed\n",
      "  - Superstep 3: Node D executed\n"
     ]
    }
   ],
   "source": [
    "import time\n",
    "from typing import Annotated\n",
    "from typing_extensions import TypedDict\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "import operator\n",
    "\n",
    "class SuperstepState(TypedDict):\n",
    "    superstep_log: Annotated[list, operator.add]\n",
    "    timestamp: str\n",
    "\n",
    "def superstep_node_a(state: SuperstepState):\n",
    "    print(f\"Superstep 1: 执行节点 A\")\n",
    "    time.sleep(0.1)  # 模拟处理时间\n",
    "    return {\"superstep_log\": [\"Superstep 1: Node A executed\"]}\n",
    "\n",
    "def superstep_node_b(state: SuperstepState):\n",
    "    print(f\"Superstep 2: 执行节点 B (并发)\")\n",
    "    time.sleep(0.2)  # 模拟处理时间\n",
    "    return {\"superstep_log\": [\"Superstep 2: Node B executed\"]}\n",
    "\n",
    "def superstep_node_c(state: SuperstepState):\n",
    "    print(f\"Superstep 2: 执行节点 C (并发)\")\n",
    "    time.sleep(0.15)  # 模拟处理时间\n",
    "    return {\"superstep_log\": [\"Superstep 2: Node C executed\"]}\n",
    "\n",
    "def superstep_node_d(state: SuperstepState):\n",
    "    print(f\"Superstep 3: 执行节点 D (同步后)\")\n",
    "    time.sleep(0.1)  # 模拟处理时间\n",
    "    return {\"superstep_log\": [\"Superstep 3: Node D executed\"]}\n",
    "\n",
    "# 构建 Superstep 演示图\n",
    "builder = StateGraph(SuperstepState)\n",
    "\n",
    "# 添加节点\n",
    "builder.add_node(\"superstep_a\", superstep_node_a)\n",
    "builder.add_node(\"superstep_b\", superstep_node_b) \n",
    "builder.add_node(\"superstep_c\", superstep_node_c)\n",
    "builder.add_node(\"superstep_d\", superstep_node_d)\n",
    "\n",
    "# 添加边：演示 Superstep 执行\n",
    "builder.add_edge(START, \"superstep_a\")          # Superstep 1: A 执行\n",
    "builder.add_edge(\"superstep_a\", \"superstep_b\")  # Superstep 2: A -> B (并发)\n",
    "builder.add_edge(\"superstep_a\", \"superstep_c\")  # Superstep 2: A -> C (并发) \n",
    "builder.add_edge(\"superstep_b\", \"superstep_d\")  # Superstep 3: B -> D (同步)\n",
    "builder.add_edge(\"superstep_c\", \"superstep_d\")  # Superstep 3: C -> D (同步)\n",
    "builder.add_edge(\"superstep_d\", END)\n",
    "\n",
    "superstep_graph = builder.compile()\n",
    "\n",
    "print(\"Superstep 执行模式演示:\")\n",
    "print(\"Superstep 1: 执行 A\")\n",
    "print(\"Superstep 2: 并发执行 B 和 C (等待同步)\")  \n",
    "print(\"Superstep 3: 同步后执行 D\")\n",
    "print()\n",
    "\n",
    "# 执行并记录时间\n",
    "start_time = time.time()\n",
    "result = superstep_graph.invoke({\"superstep_log\": []})\n",
    "end_time = time.time()\n",
    "\n",
    "print(f\"\\\\n执行完成，总时间: {end_time - start_time:.3f} 秒\")\n",
    "print(\"执行日志:\")\n",
    "for log in result[\"superstep_log\"]:\n",
    "    print(f\"  - {log}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7jzjr48b5r7",
   "metadata": {},
   "source": [
    "**💡 Superstep 核心特性**：\n",
    "\n",
    "- **并发性**：同一个 Superstep 内的多个节点可以并发执行，提高执行效率\n",
    "- **同步性**：每个 Superstep 结束时有全局同步点，保证状态更新的原子性和一致性  \n",
    "- **迭代性**：图执行是 Superstep 的迭代过程，每个 Superstep 在前一个基础上计算\n",
    "\n",
    "从上面的例子可以看到：\n",
    "- Superstep 1: 只有节点 A 执行\n",
    "- Superstep 2: 节点 B 和 C 并发执行（等待较慢的任务完成）\n",
    "- Superstep 3: 同步后节点 D 执行\n",
    "\n",
    "总执行时间约等于每个 Superstep 中最慢节点的时间之和，而非所有节点时间的总和。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "j51m8zyo5u",
   "metadata": {},
   "source": [
    "### 3.2.3 递归限制与并行分支\n",
    "\n",
    "递归限制（Recursion Limit）用于限制 LangGraph 图执行过程中的最大 Superstep 迭代次数，防止图无限循环执行。\n",
    "\n",
    "#### 重要特性\n",
    "\n",
    "- **计数单位**：递归限制的计数单位是 Superstep，而不是节点\n",
    "- **并行不影响计数**：无论一个 Superstep 内部并发执行了多少个节点，都只计为一次 Superstep 迭代\n",
    "- **防止无限循环**：有效防止图陷入无限循环，保护计算资源"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3030f305",
   "metadata": {},
   "source": [
    "##### 示例 3-19：递归限制是对并行分支流程的限制"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "65f2c6d1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "🏗️ 构建并行分支图...\n",
      "✅ 图构建完成！\n",
      "\n",
      "=== 递归限制和并行分支流程的限制 ===\n",
      "\n",
      "📊 测试 1: 正常执行 (recursion_limit=10)\n",
      "🔄 执行节点 A\n",
      "🔄 执行节点 C\n",
      "🔄 执行节点 B\n",
      "🔄 执行节点 D\n",
      "✅ 执行成功，最终结果: {'aggregate': [\"I'm A\", \"I'm B\", \"I'm C\", \"I'm D\"]}\n",
      "\n",
      "============================================================\n",
      "📊 测试 2: 递归限制过低 (recursion_limit=3)\n",
      "🔄 执行节点 A\n",
      "🔄 执行节点 B\n",
      "🔄 执行节点 C\n",
      "🔄 执行节点 D\n",
      "⚠️ 捕获 GraphRecursionError 异常: Recursion limit of 3 reached without hitting a stop condition. You can increase the limit by setting the `recursion_limit` config key.\n",
      "For troubleshooting, visit: https://python.langchain.com/docs/troubleshooting/errors/GRAPH_RECURSION_LIMIT\n",
      "📝 说明: 递归限制过低，无法完成完整的并行分支流程\n"
     ]
    }
   ],
   "source": [
    "import operator\n",
    "from typing import Annotated, Any\n",
    "from typing_extensions import TypedDict\n",
    "\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "from langgraph.errors import GraphRecursionError  # 导入 GraphRecursionError\n",
    "\n",
    "class State(TypedDict):\n",
    "    # operator.add 是状态归约器，确保状态键 aggregate 为 append-only 列表\n",
    "    aggregate: Annotated[list, operator.add]\n",
    "\n",
    "def node_a(state):\n",
    "    print(\"🔄 执行节点 A\")\n",
    "    return {\"aggregate\": [\"I'm A\"]}\n",
    "\n",
    "def node_b(state):\n",
    "    print(\"🔄 执行节点 B\")\n",
    "    return {\"aggregate\": [\"I'm B\"]}\n",
    "\n",
    "def node_c(state):\n",
    "    print(\"🔄 执行节点 C\")\n",
    "    return {\"aggregate\": [\"I'm C\"]}\n",
    "\n",
    "def node_d(state):\n",
    "    print(\"🔄 执行节点 D\")\n",
    "    return {\"aggregate\": [\"I'm D\"]}\n",
    "\n",
    "# 构建图\n",
    "print(\"🏗️ 构建并行分支图...\")\n",
    "builder = StateGraph(State)\n",
    "builder.add_node(\"a\", node_a)\n",
    "builder.add_node(\"b\", node_b)\n",
    "builder.add_node(\"c\", node_c)\n",
    "builder.add_node(\"d\", node_d)\n",
    "\n",
    "# 添加边 - 创建并行分支结构\n",
    "builder.add_edge(START, \"a\")\n",
    "builder.add_edge(\"a\", \"b\")\n",
    "builder.add_edge(\"a\", \"c\")  # 从 a 扇出到 b 和 c\n",
    "builder.add_edge(\"b\", \"d\")\n",
    "builder.add_edge(\"c\", \"d\")  # b 和 c 扇入到 d\n",
    "builder.add_edge(\"d\", END)\n",
    "\n",
    "graph = builder.compile()\n",
    "print(\"✅ 图构建完成！\")\n",
    "\n",
    "print(\"\\n=== 递归限制和并行分支流程的限制 ===\\n\")\n",
    "\n",
    "# 测试 1: 正常执行（在递归限制内）\n",
    "print(\"📊 测试 1: 正常执行 (recursion_limit=10)\")\n",
    "try:\n",
    "    result = graph.invoke({\"aggregate\": []}, {\"recursion_limit\": 10})\n",
    "    print(f\"✅ 执行成功，最终结果: {result}\")\n",
    "except Exception as e:\n",
    "    print(f\"❌ 执行失败: {e}\")\n",
    "\n",
    "print(\"\\n\" + \"=\"*60)\n",
    "\n",
    "# 测试 2: 设置较低的递归限制来触发 GraphRecursionError\n",
    "print(\"📊 测试 2: 递归限制过低 (recursion_limit=3)\")\n",
    "try:\n",
    "    # 设置 recursion_limit=3，少于完整流程所需的 Superstep 数量\n",
    "    result = graph.invoke({\"aggregate\": []}, {\"recursion_limit\": 3})\n",
    "    print(f\"✅ 执行成功，结果: {result}\")\n",
    "except GraphRecursionError as e:\n",
    "    print(f\"⚠️ 捕获 GraphRecursionError 异常: {e}\")\n",
    "    print(\"📝 说明: 递归限制过低，无法完成完整的并行分支流程\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "82544806",
   "metadata": {},
   "source": [
    "在设计包含并行分支的 LangGraph 流程时，需要根据流程的复杂度合理地设置 `recursion_limit` 参数。\n",
    "\n",
    "- 如果设置得过低，则可能导致流程在并行分支执行完成前就因为达到限制而中止，抛出 `GraphRecursionError` 异常。\n",
    "- 如果设置得过高，则可能无法有效防止流程陷入无限循环，造成不必要的计算资源消耗。\n",
    "- 建议通过实验和调优，根据流程的实际迭代次数和复杂度选择一个合适的 `recursion_limit` 值。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "br5s06thhnv",
   "metadata": {},
   "source": [
    "## 3.3 MapReduce 模式：任务分解与并行处理\n",
    "\n",
    "在构建复杂智能体系统时，经常会遇到需要处理大规模数据或执行计算密集型任务的场景。例如：\n",
    "- 批量处理海量文档进行信息提取\n",
    "- 并行生成多个创意文案\n",
    "- 分布式分析用户行为数据\n",
    "\n",
    "MapReduce (映射-归约) 模式为我们提供了一种高效、可扩展地处理这类问题的通用解决方案。\n",
    "\n",
    "### 3.3.1 MapReduce 模式的核心思想\n",
    "\n",
    "MapReduce 模式的核心思想可以用两个词概括：\"分而治之\"。它将一个复杂、大规模的计算任务分解成两个相互协作的阶段：\n",
    "\n",
    "#### Map 阶段 (映射)\n",
    "- **\"分\"的过程**：将原始的、大规模的输入数据分割成多个独立的、规模较小的子数据集\n",
    "- **并行处理**：每个子数据集分配给不同的计算节点并行处理\n",
    "- **独立执行**：每个计算节点独立地对分配的子数据集执行相同的\"映射\"操作\n",
    "\n",
    "#### Reduce 阶段 (归约) \n",
    "- **\"治\"的过程**：将 Map 阶段并行生成的多个中间结果进行\"归约\"操作\n",
    "- **聚合汇总**：将分散的、局部的中间结果合并成最终的、全局的结果\n",
    "- **整合提炼**：将 Map 阶段的\"半成品\"组装成\"成品\"\n",
    "\n",
    "#### MapReduce 的核心优势\n",
    "\n",
    "- **并行处理**：充分利用并行计算资源，显著提高数据处理效率\n",
    "- **高扩展性**：易于扩展，可以通过增加计算节点来处理更大规模的数据\n",
    "- **简化编程模型**：隐藏了底层并行计算的复杂性，开发者只需关注业务逻辑"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6752btnaevh",
   "metadata": {},
   "source": [
    "##### 示例 3-20 ~ 3-25：LangGraph 中的 MapReduce 实现"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "oofs1kxehq",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "🏗️ 构建标准 MapReduce 图...\n",
      "✅ 图构建完成！\n",
      "\n",
      "=== 🚀 MapReduce 大规模文档处理演示 ===\n",
      "📄 输入文档数量: 12\n",
      "📊 使用 Send API 实现动态任务分发\n",
      "🔄 MapReduce 流程: 分割 -> 并行映射 -> 归约\n",
      "\n",
      "============================================================\n",
      "🔄 分割节点: 将 12 个文档分割成 4 个子数据集\n",
      "📦 子数据集 0: 3 个文档\n",
      "📦 子数据集 1: 3 个文档\n",
      "📦 子数据集 2: 3 个文档\n",
      "📦 子数据集 3: 3 个文档\n",
      "🔀 路由函数: 创建 4 个并行任务\n",
      "✅ 路由完成: 创建了 4 个 Send 对象\n",
      "🔧 Map 节点: 开始处理 3 个文档\n",
      "✅ Map 节点: 处理完成，找到 33个不同单词\n",
      "🔧 Map 节点: 开始处理 3 个文档\n",
      "✅ Map 节点: 处理完成，找到 26个不同单词\n",
      "🔧 Map 节点: 开始处理 3 个文档\n",
      "✅ Map 节点: 处理完成，找到 28个不同单词\n",
      "🔧 Map 节点: 开始处理 3 个文档\n",
      "✅ Map 节点: 处理完成，找到 32个不同单词\n",
      "🔄 Reduce 节点: 汇聚 4 个中间结果\n",
      "✅ Reduce 完成: 汇总了 12 个文档\n",
      "============================================================\n",
      "\n",
      "=== ✨ MapReduce 处理结果 ===\n",
      "📊 总文档数: 12\n",
      "📝 总字符数: 1039\n",
      "🔤 不同单词数: 89\n",
      "🔢 总单词数: 130\n",
      "🏆 最高频词: 'and' (8 次)\n",
      "🥉 最低频词: 'operation' (1 次)\n",
      "\n",
      "📈 高频词汇 TOP 10:\n",
      "  📌 and: 8\n",
      "  📌 langgraph: 3\n",
      "  📌 for: 3\n",
      "  📌 with: 3\n",
      "  📌 workflows: 3\n",
      "  📌 state: 3\n",
      "  📌 capabilities: 3\n",
      "  📌 is: 2\n",
      "  📌 framework: 2\n",
      "  📌 building: 2\n"
     ]
    }
   ],
   "source": [
    "from typing import Annotated, List, Any\n",
    "from typing_extensions import TypedDict\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "from langgraph.constants import Send\n",
    "import operator\n",
    "import re\n",
    "\n",
    "# 定义整体状态结构体\n",
    "class OverallState(TypedDict):\n",
    "    # 原始大规模输入数据\n",
    "    large_input_data: List[str]\n",
    "    # 分割后的子数据集\n",
    "    sub_datasets: List[List[str]]\n",
    "    # Map 阶段的处理结果 (使用 operator.add Reducer 收集结果)\n",
    "    intermediate_results: Annotated[List[dict], operator.add]\n",
    "    # Reduce 阶段的最终结果\n",
    "    final_result: dict\n",
    "\n",
    "# 定义 Map 节点的私有状态结构体\n",
    "class MapState(TypedDict):\n",
    "    sub_data: Any  # 子任务数据类型可以是任意类型\n",
    "\n",
    "def split_large_data(input_data: List[str], num_sub_tasks: int = 10) -> List[List[str]]:\n",
    "    \"\"\"将大规模数据分割成子数据集\"\"\"\n",
    "    chunk_size = max(1, len(input_data) // num_sub_tasks)\n",
    "    chunks = []\n",
    "    for i in range(0, len(input_data), chunk_size):\n",
    "        chunks.append(input_data[i:i + chunk_size])\n",
    "    return chunks\n",
    "\n",
    "def split_input_data(state: OverallState):\n",
    "    \"\"\"分割节点函数：只负责数据分割，不返回 Send 对象\"\"\"\n",
    "    input_data = state[\"large_input_data\"]  # 从状态中获取大规模输入数据\n",
    "    sub_datasets = split_large_data(input_data, num_sub_tasks=4)  # 将大规模数据分割成子数据集\n",
    "\n",
    "    print(f\"🔄 分割节点: 将 {len(input_data)} 个文档分割成 {len(sub_datasets)} 个子数据集\")\n",
    "    for i, sub_dataset in enumerate(sub_datasets):\n",
    "        print(f\"📦 子数据集 {i}: {len(sub_dataset)} 个文档\")\n",
    "\n",
    "    return {\"sub_datasets\": sub_datasets}\n",
    "\n",
    "def route_to_map_nodes(state: OverallState):\n",
    "    \"\"\"路由函数：根据分割的数据创建 Send 对象\"\"\"\n",
    "    sub_datasets = state[\"sub_datasets\"]\n",
    "\n",
    "    print(f\"🔀 路由函数: 创建 {len(sub_datasets)} 个并行任务\")\n",
    "\n",
    "    send_list = []\n",
    "    for i, sub_dataset in enumerate(sub_datasets):  # 遍历每个子数据集\n",
    "        send_list.append(\n",
    "            Send(\"map_node\", {\"sub_data\": sub_dataset})  # 为每个子数据集创建一个 Send 对象\n",
    "        )\n",
    "\n",
    "    print(f\"✅ 路由完成: 创建了 {len(send_list)} 个 Send 对象\")\n",
    "    return send_list  # 返回 Send 对象列表，用于动态路由到多个 Map 节点实例\n",
    "\n",
    "def process_sub_data(sub_data: List[str]) -> dict:\n",
    "    \"\"\"处理子任务数据，生成中间结果\"\"\"\n",
    "    word_count = {}\n",
    "    total_chars = 0\n",
    "\n",
    "    for doc in sub_data:\n",
    "        # 统计词频\n",
    "        words = re.findall(r'\\b\\w+\\b', doc.lower())\n",
    "        for word in words:\n",
    "            word_count[word] = word_count.get(word, 0) + 1\n",
    "        # 统计字符数\n",
    "        total_chars += len(doc)\n",
    "\n",
    "    return {\n",
    "        \"word_count\": word_count,\n",
    "        \"doc_count\": len(sub_data),\n",
    "        \"total_chars\": total_chars,\n",
    "        \"unique_words\": len(word_count)\n",
    "    }\n",
    "\n",
    "def map_node(state: MapState):\n",
    "    \"\"\"Map 节点函数，输入状态为 MapState\"\"\"\n",
    "    sub_data = state[\"sub_data\"]  # 从状态中获取子任务数据\n",
    "    print(f\"🔧 Map 节点: 开始处理 {len(sub_data)} 个文档\")\n",
    "\n",
    "    intermediate_result = process_sub_data(sub_data)  # 处理子任务数据，生成中间结果\n",
    "\n",
    "    print(f\"✅ Map 节点: 处理完成，找到 {intermediate_result['unique_words']}个不同单词\")\n",
    "\n",
    "    return {\"intermediate_results\": [intermediate_result]}  # 返回中间结果，用于后续 Reduce 阶段聚合\n",
    "\n",
    "def aggregate_results(intermediate_results: List[dict]) -> dict:\n",
    "    \"\"\"聚合中间结果，生成最终结果\"\"\"\n",
    "    global_word_count = {}\n",
    "    total_docs = 0\n",
    "    total_chars = 0\n",
    "\n",
    "    for result in intermediate_results:\n",
    "        total_docs += result[\"doc_count\"]\n",
    "        total_chars += result[\"total_chars\"]\n",
    "\n",
    "        # 合并词频统计\n",
    "        for word, count in result[\"word_count\"].items():\n",
    "            global_word_count[word] = global_word_count.get(word, 0) + count\n",
    "\n",
    "    # 找出最高频和最低频的词\n",
    "    if global_word_count:\n",
    "        sorted_words = sorted(global_word_count.items(), key=lambda x: x[1],\n",
    "reverse=True)\n",
    "        most_common = sorted_words[0]\n",
    "        least_common = sorted_words[-1]\n",
    "    else:\n",
    "        most_common = (\"\", 0)\n",
    "        least_common = (\"\", 0)\n",
    "\n",
    "    return {\n",
    "        \"total_documents\": total_docs,\n",
    "        \"total_characters\": total_chars,\n",
    "        \"total_unique_words\": len(global_word_count),\n",
    "        \"total_words\": sum(global_word_count.values()),\n",
    "        \"most_common_word\": most_common,\n",
    "        \"least_common_word\": least_common,\n",
    "        \"word_distribution\": dict(sorted_words[:10])  # 只保留前10个高频词\n",
    "    }\n",
    "\n",
    "def reduce_node(state: OverallState):\n",
    "    \"\"\"Reduce 节点函数，输入状态为 OverallState\"\"\"\n",
    "    intermediate_results = state[\"intermediate_results\"]  # 从状态中获取 Map 阶段生成的中间结果列表\n",
    "\n",
    "    print(f\"🔄 Reduce 节点: 汇聚 {len(intermediate_results)} 个中间结果\")\n",
    "\n",
    "    final_result = aggregate_results(intermediate_results)  # 聚合中间结果，生成最终结果\n",
    "\n",
    "    print(f\"✅ Reduce 完成: 汇总了 {final_result['total_documents']} 个文档\")\n",
    "\n",
    "    return {\"final_result\": final_result}  # 返回最终结果\n",
    "\n",
    "# 构建 MapReduce 图\n",
    "print(\"🏗️ 构建标准 MapReduce 图...\")\n",
    "builder = StateGraph(OverallState)\n",
    "\n",
    "# 添加节点\n",
    "builder.add_node(\"split_node\", split_input_data)\n",
    "builder.add_node(\"map_node\", map_node)\n",
    "builder.add_node(\"reduce_node\", reduce_node)\n",
    "\n",
    "# 连接 MapReduce 流程中的节点和边\n",
    "builder.add_edge(START, \"split_node\")\n",
    "\n",
    "# 关键修正：分离数据分割和任务路由\n",
    "# 分割节点 -> Map 节点 (条件边, 使用专门的路由函数)\n",
    "builder.add_conditional_edges(\"split_node\", route_to_map_nodes, [\"map_node\"])\n",
    "\n",
    "# Map 节点 -> Reduce 节点 (普通边)\n",
    "builder.add_edge(\"map_node\", \"reduce_node\")\n",
    "\n",
    "# Reduce 节点 -> END (普通边)\n",
    "builder.add_edge(\"reduce_node\", END)\n",
    "\n",
    "mapreduce_graph = builder.compile()\n",
    "print(\"✅ 图构建完成！\")\n",
    "\n",
    "# 测试数据：模拟大规模文档数据\n",
    "large_documents = [\n",
    "    \"LangGraph is a powerful framework for building AI agent systems with complex workflows.\",\n",
    "    \"The framework provides comprehensive state management and advanced flow control capabilities.\",\n",
    "    \"Parallel processing in LangGraph enables efficient task execution and resource utilization.\",\n",
    "    \"MapReduce pattern helps process large datasets effectively using distributed computing principles.\",\n",
    "    \"AI agents can use various tools and manage complex workflows with sophisticated coordination.\",\n",
    "    \"State management is crucial for building reliable and scalable distributed systems.\",\n",
    "    \"LangGraph supports dynamic branching with Send API for flexible workflowdesign.\",\n",
    "    \"Concurrent execution improves overall system performance and throughput significantly.\",\n",
    "    \"The Send API enables dynamic task distribution and parallel processing capabilities.\",\n",
    "    \"Reducer functions ensure safe concurrent state updates in multi-threadedenvironments.\",\n",
    "    \"Graph-based workflows provide clear visualization and better debugging capabilities.\",\n",
    "    \"Advanced error handling and retry mechanisms ensure robust system operation.\"\n",
    "]\n",
    "\n",
    "print(\"\\n=== 🚀 MapReduce 大规模文档处理演示 ===\")\n",
    "print(f\"📄 输入文档数量: {len(large_documents)}\")\n",
    "print(f\"📊 使用 Send API 实现动态任务分发\")\n",
    "print(f\"🔄 MapReduce 流程: 分割 -> 并行映射 -> 归约\")\n",
    "print(\"\\n\" + \"=\"*60)\n",
    "\n",
    "# 执行 MapReduce 流程\n",
    "result = mapreduce_graph.invoke({\n",
    "    \"large_input_data\": large_documents,\n",
    "    \"sub_datasets\": [],\n",
    "    \"intermediate_results\": [],\n",
    "    \"final_result\": {}\n",
    "})\n",
    "\n",
    "print(\"=\"*60)\n",
    "print(\"\\n=== ✨ MapReduce 处理结果 ===\")\n",
    "final_result = result[\"final_result\"]\n",
    "print(f\"📊 总文档数: {final_result['total_documents']}\")\n",
    "print(f\"📝 总字符数: {final_result['total_characters']}\")\n",
    "print(f\"🔤 不同单词数: {final_result['total_unique_words']}\")\n",
    "print(f\"🔢 总单词数: {final_result['total_words']}\")\n",
    "print(f\"🏆 最高频词: '{final_result['most_common_word'][0]}' ({final_result['most_common_word'][1]} 次)\")\n",
    "print(f\"🥉 最低频词: '{final_result['least_common_word'][0]}' ({final_result['least_common_word'][1]} 次)\")\n",
    "\n",
    "print(f\"\\n📈 高频词汇 TOP 10:\")\n",
    "for word, count in final_result['word_distribution'].items():\n",
    "    print(f\"  📌 {word}: {count}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "lust6ioew1o",
   "metadata": {},
   "source": [
    "**💡 MapReduce 实现核心要点**：\n",
    "\n",
    "#### 流程设计\n",
    "1. **分割阶段 (Split)**：将输入文档按块大小分割，为并行处理做准备\n",
    "2. **映射阶段 (Map)**：多个 Map 节点并发处理不同的数据块，执行词频统计\n",
    "3. **归约阶段 (Reduce)**：汇聚所有 Map 结果，生成全局统计\n",
    "\n",
    "#### 实际应用场景\n",
    "- **文档分析**：批量处理大量文档进行内容分析\n",
    "- **数据挖掘**：从海量数据中提取统计信息\n",
    "- **并行计算**：任何可以分割处理的计算密集型任务\n",
    "\n",
    "这个例子展示了如何将传统的 MapReduce 思想与 LangGraph 的图计算模型相结合，构建高效的并行数据处理流程。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "i1p8k8um7mi",
   "metadata": {},
   "source": [
    "## 3.4 子图机制：模块化、复用与复杂性管理\n",
    "\n",
    "在构建日益复杂的 AI 智能体系统时，模块化 (Modularity) 和复用 (Reuse) 变得至关重要。LangGraph 框架通过子图 (Subgraph) 机制，为我们提供了强大的模块化和复用能力，使得我们可以像搭积木一样构建复杂的智能体系统。\n",
    "\n",
    "### 3.4.1 子图 (Subgraph) 的概念与优势\n",
    "\n",
    "子图 (Subgraph) 在 LangGraph 中，指的是一个\"嵌套\"在另一个 LangGraph 图 (父图，Parent Graph) 内部的图结构。子图本质上仍然是一个 LangGraph 图，但它被\"封装\"在父图内部，作为父图的一个组成部分。\n",
    "\n",
    "#### 子图的核心优势：\n",
    "\n",
    "- **模块化**：将复杂系统分解成多个独立的、职责单一的子图模块\n",
    "- **复用性**：子图作为独立模块，可以在不同的父图中重复使用  \n",
    "- **状态隔离**：子图拥有独立的状态结构体和状态空间，与父图状态相互隔离\n",
    "- **命名空间管理**：避免不同子图或父图中节点名称的冲突\n",
    "- **简化复杂性**：通过层层嵌套和模块化组合，有效控制系统复杂性"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "nkn4cj8ozc",
   "metadata": {},
   "source": [
    "### 3.4.2 LangGraph 中定义和使用子图\n",
    "\n",
    "在 LangGraph 中，定义和使用子图主要有两种方式：\n",
    "\n",
    "#### 方式 1：将已编译的子图作为节点添加到父图\n",
    "\n",
    "这是最简单、最直接的子图使用方式。如果父图和子图之间需要共享状态键（例如，共享 `messages` 对话历史状态键），可以直接将已编译的子图作为特殊节点添加到父图中。\n",
    "\n",
    "#### 方式 2：使用节点函数调用子图，并进行状态转换\n",
    "\n",
    "对于更复杂的系统，父图和子图的状态结构体可能完全不同。这时需要创建节点函数，在函数内部调用子图，并进行状态转换。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bmmrjjz7yyc",
   "metadata": {},
   "source": [
    "##### 示例 3-26：子图模块化设计示例"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "qfuf04x3rmc",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "数据处理子图构建完成\n",
      "流程: START -> validate -> clean -> transform -> END\n",
      "报告生成子图构建完成\n",
      "流程: START -> generate_title -> create_report -> END\n",
      "主工作流构建完成\n",
      "流程: START -> data_processing(子图1) -> report_generation(子图2) -> END\n",
      "\n",
      "=== 测试子图模块化系统 ===\n",
      "\n",
      "输入数据: Hello, World! This is sample data for processing.\n",
      "工作流状态: Report generation completed\n",
      "\n",
      "最终报告:\n",
      "Data Analysis Report: PROCESSED: HELLO WOR...\n",
      "    \n",
      "Data Content: PROCESSED: HELLO WORLD! THIS IS SAMPLE DATA FOR PROCESSING.\n",
      "Report Generated: Successfully processed data\n",
      "Status: Complete\n",
      "\n"
     ]
    }
   ],
   "source": [
    "from typing_extensions import TypedDict\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "\n",
    "# ===== 定义子图 1：数据处理子图 =====\n",
    "class DataProcessingState(TypedDict):\n",
    "    input_data: str\n",
    "    processed_data: str\n",
    "    processing_steps: list\n",
    "\n",
    "def validate_data(state: DataProcessingState):\n",
    "    \"\"\"数据验证节点\"\"\"\n",
    "    input_data = state[\"input_data\"]\n",
    "    is_valid = len(input_data.strip()) > 0\n",
    "    return {\n",
    "        \"processing_steps\": [f\"Validation: {'Passed' if is_valid else 'Failed'}\"],\n",
    "        \"processed_data\": input_data if is_valid else \"\"\n",
    "    }\n",
    "\n",
    "def clean_data(state: DataProcessingState):\n",
    "    \"\"\"数据清洗节点\"\"\"\n",
    "    data = state[\"processed_data\"]\n",
    "    cleaned_data = data.strip().lower().replace(\",\", \"\")\n",
    "    return {\n",
    "        \"processing_steps\": [f\"Cleaning: Removed whitespace and special chars\"],\n",
    "        \"processed_data\": cleaned_data\n",
    "    }\n",
    "\n",
    "def transform_data(state: DataProcessingState):\n",
    "    \"\"\"数据转换节点\"\"\"\n",
    "    data = state[\"processed_data\"]\n",
    "    transformed_data = f\"PROCESSED: {data.upper()}\"\n",
    "    return {\n",
    "        \"processing_steps\": [f\"Transform: Converted to uppercase with prefix\"],\n",
    "        \"processed_data\": transformed_data\n",
    "    }\n",
    "\n",
    "# 构建数据处理子图\n",
    "data_processing_builder = StateGraph(DataProcessingState)\n",
    "data_processing_builder.add_node(\"validate\", validate_data)\n",
    "data_processing_builder.add_node(\"clean\", clean_data)  \n",
    "data_processing_builder.add_node(\"transform\", transform_data)\n",
    "\n",
    "data_processing_builder.add_edge(START, \"validate\")\n",
    "data_processing_builder.add_edge(\"validate\", \"clean\")\n",
    "data_processing_builder.add_edge(\"clean\", \"transform\")\n",
    "data_processing_builder.add_edge(\"transform\", END)\n",
    "\n",
    "# 编译数据处理子图\n",
    "data_processing_subgraph = data_processing_builder.compile()\n",
    "\n",
    "print(\"数据处理子图构建完成\")\n",
    "print(\"流程: START -> validate -> clean -> transform -> END\")\n",
    "\n",
    "# ===== 定义子图 2：报告生成子图 =====\n",
    "class ReportState(TypedDict):\n",
    "    processed_data: str\n",
    "    report_title: str\n",
    "    final_report: str\n",
    "\n",
    "def generate_title(state: ReportState):\n",
    "    \"\"\"生成报告标题\"\"\"\n",
    "    processed_data = state[\"processed_data\"]\n",
    "    title = f\"Data Analysis Report: {processed_data[:20]}...\"\n",
    "    return {\"report_title\": title}\n",
    "\n",
    "def create_report(state: ReportState):\n",
    "    \"\"\"创建完整报告\"\"\"\n",
    "    title = state[\"report_title\"]  \n",
    "    data = state[\"processed_data\"]\n",
    "    report = f\"\"\"{title}\n",
    "    \n",
    "Data Content: {data}\n",
    "Report Generated: Successfully processed data\n",
    "Status: Complete\n",
    "\"\"\"\n",
    "    return {\"final_report\": report}\n",
    "\n",
    "# 构建报告生成子图  \n",
    "report_builder = StateGraph(ReportState)\n",
    "report_builder.add_node(\"generate_title\", generate_title)\n",
    "report_builder.add_node(\"create_report\", create_report)\n",
    "\n",
    "report_builder.add_edge(START, \"generate_title\")\n",
    "report_builder.add_edge(\"generate_title\", \"create_report\") \n",
    "report_builder.add_edge(\"create_report\", END)\n",
    "\n",
    "# 编译报告生成子图\n",
    "report_subgraph = report_builder.compile()\n",
    "\n",
    "print(\"报告生成子图构建完成\")\n",
    "print(\"流程: START -> generate_title -> create_report -> END\")\n",
    "\n",
    "# ===== 定义父图：整合两个子图 =====\n",
    "class MainWorkflowState(TypedDict):\n",
    "    raw_input: str\n",
    "    workflow_status: str\n",
    "    final_output: str\n",
    "\n",
    "def call_data_processing(state: MainWorkflowState):\n",
    "    \"\"\"调用数据处理子图的节点函数\"\"\"\n",
    "    # 状态转换：父图状态 -> 子图状态\n",
    "    subgraph_input = {\n",
    "        \"input_data\": state[\"raw_input\"],\n",
    "        \"processed_data\": \"\",\n",
    "        \"processing_steps\": []\n",
    "    }\n",
    "    \n",
    "    # 调用数据处理子图\n",
    "    subgraph_output = data_processing_subgraph.invoke(subgraph_input)\n",
    "    \n",
    "    # 状态转换：子图状态 -> 父图状态  \n",
    "    return {\n",
    "        \"workflow_status\": \"Data processing completed\",\n",
    "        \"final_output\": subgraph_output[\"processed_data\"]\n",
    "    }\n",
    "\n",
    "def call_report_generation(state: MainWorkflowState):\n",
    "    \"\"\"调用报告生成子图的节点函数\"\"\"\n",
    "    # 状态转换：父图状态 -> 子图状态\n",
    "    subgraph_input = {\n",
    "        \"processed_data\": state[\"final_output\"],\n",
    "        \"report_title\": \"\",\n",
    "        \"final_report\": \"\"\n",
    "    }\n",
    "    \n",
    "    # 调用报告生成子图\n",
    "    subgraph_output = report_subgraph.invoke(subgraph_input)\n",
    "    \n",
    "    # 状态转换：子图状态 -> 父图状态\n",
    "    return {\n",
    "        \"workflow_status\": \"Report generation completed\", \n",
    "        \"final_output\": subgraph_output[\"final_report\"]\n",
    "    }\n",
    "\n",
    "# 构建主工作流父图\n",
    "main_builder = StateGraph(MainWorkflowState)\n",
    "main_builder.add_node(\"data_processing\", call_data_processing)\n",
    "main_builder.add_node(\"report_generation\", call_report_generation)\n",
    "\n",
    "main_builder.add_edge(START, \"data_processing\")\n",
    "main_builder.add_edge(\"data_processing\", \"report_generation\")  \n",
    "main_builder.add_edge(\"report_generation\", END)\n",
    "\n",
    "# 编译主工作流\n",
    "main_workflow = main_builder.compile()\n",
    "\n",
    "print(\"主工作流构建完成\")\n",
    "print(\"流程: START -> data_processing(子图1) -> report_generation(子图2) -> END\")\n",
    "\n",
    "# ===== 测试子图模块化系统 =====\n",
    "print(\"\\n=== 测试子图模块化系统 ===\")\n",
    "test_input = \"Hello, World! This is sample data for processing.\"\n",
    "\n",
    "result = main_workflow.invoke({\n",
    "    \"raw_input\": test_input,\n",
    "    \"workflow_status\": \"\",\n",
    "    \"final_output\": \"\"\n",
    "})\n",
    "\n",
    "print(f\"\\n输入数据: {test_input}\")\n",
    "print(f\"工作流状态: {result['workflow_status']}\")\n",
    "print(\"\\n最终报告:\")\n",
    "print(result[\"final_output\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "80edb534",
   "metadata": {},
   "source": [
    "##### 示例 3-27：使用节点函数调用子图并进行状态转换"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "925efec9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "🏗️ 构建子图...\n",
      "✅ 子图编译完成\n",
      "\n",
      "🏗️ 构建父图...\n",
      "✅ 父图构建完成！\n",
      "\n",
      "📥 初始父图状态: {'my_key': '用户输入数据'}\n",
      "\n",
      "============================================================\n",
      "🔧 父图节点1: 预处理 '用户输入数据'\n",
      "🔄 调用子图节点: 接收父图状态 '父图预处理: 用户输入数据'\n",
      "🔀 状态转换: 父图状态 -> 子图状态\n",
      "   输入子图: {'my_child_key': '父图预处理: 用户输入数据'}\n",
      "🔧 子图节点1: 处理输入 '父图预处理: 用户输入数据'\n",
      "🔧 子图节点2: 进一步处理 '子图处理: 父图预处理: 用户输入数据'\n",
      "📤 子图输出: {'my_child_key': '子图处理: 父图预处理: 用户输入数据 -> 完成处理'}\n",
      "🔀 状态转换: 子图状态 -> 父图状态\n",
      "   返回父图: {'my_key': '子图处理: 父图预处理: 用户输入数据 -> 完成处理'}\n",
      "🔧 父图节点2: 后处理 '子图处理: 父图预处理: 用户输入数据 -> 完成处理'\n",
      "============================================================\n",
      "\n",
      "📤 最终父图状态: {'my_key': '子图处理: 父图预处理: 用户输入数据 -> 完成处理 -> 父图完成'}\n"
     ]
    }
   ],
   "source": [
    "from typing_extensions import TypedDict\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "\n",
    "# 模拟子图 child_graph 的定义和编译（这里用简化版本）\n",
    "class ChildState(TypedDict):\n",
    "    my_child_key: str\n",
    "\n",
    "def child_node_1(state: ChildState):\n",
    "    print(f\"🔧 子图节点1: 处理输入 '{state['my_child_key']}'\")\n",
    "    return {\"my_child_key\": f\"子图处理: {state['my_child_key']}\"}\n",
    "\n",
    "def child_node_2(state: ChildState):\n",
    "    print(f\"🔧 子图节点2: 进一步处理 '{state['my_child_key']}'\")\n",
    "    return {\"my_child_key\": f\"{state['my_child_key']} -> 完成处理\"}\n",
    "\n",
    "# 构建子图\n",
    "print(\"🏗️ 构建子图...\")\n",
    "child_builder = StateGraph(ChildState)\n",
    "child_builder.add_node(\"child_1\", child_node_1)\n",
    "child_builder.add_node(\"child_2\", child_node_2)\n",
    "child_builder.add_edge(START, \"child_1\")\n",
    "child_builder.add_edge(\"child_1\", \"child_2\")\n",
    "child_builder.add_edge(\"child_2\", END)\n",
    "\n",
    "child_graph = child_builder.compile()\n",
    "print(\"✅ 子图编译完成\")\n",
    "\n",
    "# 定义父图\n",
    "class ParentState(TypedDict):  # 父图的状态结构体 \n",
    "    my_key: str  # 父图状态键: my_key\n",
    "\n",
    "def call_child_graph(state: ParentState) -> ParentState:  # 节点函数，输入输出状态类型都为 ParentState\n",
    "    print(f\"🔄 调用子图节点: 接收父图状态 '{state['my_key']}'\")\n",
    "\n",
    "    # 状态转换: 父图状态 -> 子图状态 (ParentState.my_key -> ChildState.my_child_key)\n",
    "    child_graph_input = {\"my_child_key\": state[\"my_key\"]}  # 将父图状态的 my_key 值，赋值给子图状态的 my_child_key 键\n",
    "    print(f\"🔀 状态转换: 父图状态 -> 子图状态\")\n",
    "    print(f\"   输入子图: {child_graph_input}\")\n",
    "\n",
    "    # 调用子图\n",
    "    child_graph_output = child_graph.invoke(child_graph_input)  # 调用子图 child_graph\n",
    "    print(f\"📤 子图输出: {child_graph_output}\")\n",
    "\n",
    "    # 状态转换: 子图状态 -> 父图状态 (ChildState.my_child_key -> ParentState.my_key)\n",
    "    result = {\"my_key\": child_graph_output[\"my_child_key\"]}  # 将子图输出状态的 my_child_key 值，赋值给父图状态的 my_key 键\n",
    "    print(f\"🔀 状态转换: 子图状态 -> 父图状态\")\n",
    "    print(f\"   返回父图: {result}\")\n",
    "\n",
    "    return result\n",
    "\n",
    "def parent_node_1(state: ParentState):\n",
    "    print(f\"🔧 父图节点1: 预处理 '{state['my_key']}'\")\n",
    "    return {\"my_key\": f\"父图预处理: {state['my_key']}\"}\n",
    "\n",
    "def parent_node_2(state: ParentState):\n",
    "    print(f\"🔧 父图节点2: 后处理 '{state['my_key']}'\")\n",
    "    return {\"my_key\": f\"{state['my_key']} -> 父图完成\"}\n",
    "\n",
    "print(\"\\n🏗️ 构建父图...\")\n",
    "builder = StateGraph(ParentState)  # 创建父图 StateGraph，指定状态结构体为 \n",
    "ParentState\n",
    "\n",
    "# 父图的其他节点定义\n",
    "builder.add_node(\"parent_1\", parent_node_1)\n",
    "builder.add_node(\"parent_2\", parent_node_2)\n",
    "builder.add_node(\"child\", call_child_graph)  # 将节点函数 call_child_graph 作为节点添加到父图，节点名为 \"child\"\n",
    "\n",
    "builder.add_edge(START, \"parent_1\")  # 父图节点 parent_1 -> 节点函数 call_child_graph (普通边)\n",
    "builder.add_edge(\"parent_1\", \"child\")\n",
    "builder.add_edge(\"child\", \"parent_2\")\n",
    "builder.add_edge(\"parent_2\", END)\n",
    "\n",
    "graph = builder.compile()  # 编译父图\n",
    "print(\"✅ 父图构建完成！\")\n",
    "\n",
    "# 测试运行\n",
    "initial_state = {\"my_key\": \"用户输入数据\"}\n",
    "print(f\"\\n📥 初始父图状态: {initial_state}\")\n",
    "print(\"\\n\" + \"=\"*60)\n",
    "\n",
    "result = graph.invoke(initial_state)\n",
    "\n",
    "print(\"=\"*60)\n",
    "print(f\"\\n📤 最终父图状态: {result}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "22458d3e",
   "metadata": {},
   "source": [
    "🔄 核心价值\n",
    "\n",
    "- 灵活性: 支持父子图使用完全不同的状态结构体\n",
    "- 模块化: 实现复杂系统的模块化设计\n",
    "- 解耦合: 不同组件可以独立设计状态结构\n",
    "\n",
    "🎯 典型应用场景\n",
    "\n",
    "多智能体RAG系统示例：\n",
    "- 父图(Supervisor Agent): 关注最终RAG报告\n",
    "- 子图(ReAct Agent): 维护详细对话历史和工具调用记录\n",
    "\n",
    "🔀 状态转换四步流程\n",
    "\n",
    "1. 输入转换: 父图状态 → 子图状态\n",
    "2. 子图处理: 子图内部执行业务逻辑\n",
    "3. 输出转换: 子图状态 → 父图状态\n",
    "4. 状态更新: 返回转换后的父图状态\n",
    "\n",
    "✅ 选择策略\n",
    "\n",
    "- 共享状态键 → 直接将子图作为节点添加\n",
    "- 异构状态结构 → 使用节点函数+状态转换方式\n",
    "- 复杂多智能体系统 → 各智能体独立状态结构体\n",
    "\n",
    "关键优势: 实现了状态结构的完全解耦，支持复杂系统的分层设计和模块化开发。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "rmkyk31y4e",
   "metadata": {},
   "source": [
    "**💡 子图机制核心价值**：\n",
    "\n",
    "#### 模块化设计\n",
    "- **数据处理子图**：封装了数据验证、清洗、转换的完整流程，可以在不同场景中复用\n",
    "- **报告生成子图**：专门负责报告生成逻辑，独立维护和测试\n",
    "- **主工作流**：通过组合不同子图，构建完整的数据处理管道\n",
    "\n",
    "#### 状态隔离与转换\n",
    "- 每个子图维护自己的状态结构\n",
    "- 通过节点函数进行状态转换，实现父图与子图之间的数据适配\n",
    "- 状态隔离避免了模块间的相互干扰\n",
    "\n",
    "#### 复用与扩展\n",
    "- 子图可以在不同的父图中复用，提高开发效率\n",
    "- 新的处理步骤可以通过添加新子图或修改现有子图来实现\n",
    "- 模块化设计使系统易于维护和扩展"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "oqdf6x1e7k",
   "metadata": {},
   "source": [
    "## 3.5 工具调用：扩展智能体的能力边界\n",
    "\n",
    "在构建智能体系统的过程中，工具调用 (Tool Calling) 是一项至关重要的能力。智能体通过调用各种外部工具，可以扩展自身的能力边界，完成更复杂、更实用的任务。\n",
    "\n",
    "LangGraph 框架提供了强大的工具调用支持，并预置了 `ToolNode` 组件，极大地简化了在 LangGraph 图中集成和使用工具的流程。\n",
    "\n",
    "### 3.5.1 ToolNode：LangGraph 的工具调用中心\n",
    "\n",
    "`ToolNode` 是 LangGraph 框架预置的核心组件，专门用于处理工具调用操作。可以将其理解为 LangGraph 图中的\"工具执行器\"或\"工具调度中心\"。\n",
    "\n",
    "#### ToolNode 的核心职责：\n",
    "\n",
    "1. **接收来自 LLM 的工具调用请求**：与支持工具调用的 LLM 模型集成，接收工具调用请求\n",
    "2. **执行工具调用**：动态调度和调用预先注册的工具，并返回执行结果  \n",
    "3. **更新图状态**：将工具执行结果封装成 `ToolMessage` 并添加到图状态中\n",
    "\n",
    "#### 使用 ToolNode 的优势：\n",
    "\n",
    "- **简化工具集成**：封装工具调用复杂性，开发者只需简单注册工具列表\n",
    "- **原生支持 LangChain 工具**：与 LangChain 工具体系无缝集成\n",
    "- **内置错误处理**：自动捕获工具执行异常，提高系统鲁棒性\n",
    "- **兼容 ReAct Agent**：与 LangGraph 预置的 ReAct 组件高度兼容"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "m2y219n8kbj",
   "metadata": {},
   "source": [
    "##### 示例 3-28 & 3-29：使用 ToolNode 构建工具调用智能体"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "12394k6achoa",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'messages': [ToolMessage(content=\"It's 60 degrees and foggy.\", name='get_weather', tool_call_id='tool_call_id')]}\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.tools import tool\n",
    "from langchain_core.messages import AIMessage\n",
    "from langgraph.prebuilt import ToolNode\n",
    "\n",
    "@tool # 使用 @tool 装饰器，将 Python 函数转换为 LangChain Tool\n",
    "def get_weather(location: str): # 定义工具函数 get_weather, location 参数用于接收城市名称\n",
    "    \"\"\"Call to get the current weather.\"\"\" # 工具函数的 docstring 会被作为工具的描述信息，提供给 LLM\n",
    "    if location.lower() in [\"sf\", \"san francisco\"]:\n",
    "        return \"It's 60 degrees and foggy.\"\n",
    "    else:\n",
    "        return \"It's 90 degrees and sunny.\"\n",
    "\n",
    "@tool\n",
    "def get_coolest_cities():\n",
    "    \"\"\"Get a list of coolest cities\"\"\"\n",
    "    return \"nyc, sf\"\n",
    "\n",
    "\n",
    "\n",
    "# 创建 ToolNode 实例，注册工具列表 (包含 get_weather 和 get_coolest_cities 两个工具)\n",
    "tools = [get_weather, get_coolest_cities]\n",
    "tool_node = ToolNode(tools)\n",
    "\n",
    "# 构造包含单个工具调用请求的 AIMessage\n",
    "message_with_single_tool_call = AIMessage( # 创建 AIMessage\n",
    "    content=\"\", # content 为空字符串，表示该消息主要用于工具调用，不包含文本内容\n",
    "    tool_calls=[ # tool_calls 参数，包含工具调用请求列表\n",
    "        {\n",
    "            \"name\": \"get_weather\", #  工具名称，必须与注册的工具名称一致\n",
    "            \"args\": {\"location\": \"sf\"}, #  工具参数，必须与工具函数定义的参数匹配\n",
    "            \"id\": \"tool_call_id\", #  工具调用 ID，用于唯一标识工具调用，  可以自定义\n",
    "            \"type\": \"tool_call\", #  消息类型，固定为 \"tool_call\"\n",
    "        }\n",
    "    ],\n",
    ")\n",
    "\n",
    "# 手动调用 ToolNode，输入为包含 AIMessage 的状态字典\n",
    "tool_node_output = tool_node.invoke({\"messages\": [message_with_single_tool_call]})\n",
    "\n",
    "print(tool_node_output) # 打印 ToolNode 的输出结果"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e27e1dce",
   "metadata": {},
   "source": [
    "##### 示例 3-30：手动调用 ToolNode（并行工具调用）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8b7384c6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'messages': [ToolMessage(content='nyc, sf', name='get_coolest_cities', tool_call_id='tool_call_id_1'), ToolMessage(content=\"It's 60 degrees and foggy.\", name='get_weather', tool_call_id='tool_call_id_2')]}\n"
     ]
    }
   ],
   "source": [
    "# 构造包含多个工具调用请求的 AIMessage\n",
    "message_with_multiple_tool_calls = AIMessage( # 创建 AIMessage\n",
    "    content=\"\",\n",
    "    tool_calls=[ # tool_calls 参数，包含多个工具调用请求\n",
    "        {\n",
    "            \"name\": \"get_coolest_cities\", # 工具 1：get_coolest_cities\n",
    "            \"args\": {},\n",
    "            \"id\": \"tool_call_id_1\",\n",
    "            \"type\": \"tool_call\",\n",
    "        },\n",
    "        {\n",
    "            \"name\": \"get_weather\", # 工具 2：get_weather\n",
    "            \"args\": {\"location\": \"sf\"},\n",
    "            \"id\": \"tool_call_id_2\",\n",
    "            \"type\": \"tool_call\",\n",
    "        },\n",
    "    ],\n",
    ")\n",
    "\n",
    "# 手动调用 ToolNode,  输入为包含 AIMessage 的状态字典\n",
    "tool_node_output = tool_node.invoke({\"messages\": [message_with_multiple_tool_calls]})\n",
    "\n",
    "print(tool_node_output) # 打印 ToolNode 的输出结果"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9c18ae78",
   "metadata": {},
   "source": [
    "##### 示例 3-31：在 LangGraph 图中使用 ToolNode 构建 ReAct 智能体"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f04a94d6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'messages': [HumanMessage(content='what is the weather in sf', additional_kwargs={}, response_metadata={}, id='34ecd7e5-d783-432d-8e10-b88e07ddaf62'),\n",
       "  AIMessage(content='\\n\\n', additional_kwargs={'tool_calls': [{'id': '0198c62a207bdc8ef9a5afb762ec5225', 'function': {'arguments': ' {\"location\": \"sf\"}', 'name': 'get_weather'}, 'type': 'function', 'index': 0}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 156, 'prompt_tokens': 204, 'total_tokens': 360, 'completion_tokens_details': {'accepted_prediction_tokens': None, 'audio_tokens': None, 'reasoning_tokens': 148, 'rejected_prediction_tokens': None}, 'prompt_tokens_details': None}, 'model_name': 'Qwen/Qwen3-8B', 'system_fingerprint': '', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run--f86d6a8c-d6a8-4a73-80df-70618ced2b13-0', tool_calls=[{'name': 'get_weather', 'args': {'location': 'sf'}, 'id': '0198c62a207bdc8ef9a5afb762ec5225', 'type': 'tool_call'}], usage_metadata={'input_tokens': 204, 'output_tokens': 156, 'total_tokens': 360, 'input_token_details': {}, 'output_token_details': {'reasoning': 148}}),\n",
       "  ToolMessage(content=\"It's 60 degrees and foggy.\", name='get_weather', id='55196e1a-c8e4-40c3-ba2a-3dae67151012', tool_call_id='0198c62a207bdc8ef9a5afb762ec5225'),\n",
       "  AIMessage(content=\"\\n\\nThe current weather in San Francisco is 60 degrees Fahrenheit and foggy. Be sure to carry an umbrella if you're heading out!\", additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 184, 'prompt_tokens': 246, 'total_tokens': 430, 'completion_tokens_details': {'accepted_prediction_tokens': None, 'audio_tokens': None, 'reasoning_tokens': 155, 'rejected_prediction_tokens': None}, 'prompt_tokens_details': None}, 'model_name': 'Qwen/Qwen3-8B', 'system_fingerprint': '', 'finish_reason': 'stop', 'logprobs': None}, id='run--e85daa98-1594-4ef6-a759-c52de36c9fa6-0', usage_metadata={'input_tokens': 246, 'output_tokens': 184, 'total_tokens': 430, 'input_token_details': {}, 'output_token_details': {'reasoning': 155}})]}"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langgraph.graph import StateGraph, MessagesState, START, END # 导入 MessagesState\n",
    "from langgraph.prebuilt import ToolNode # 导入 ToolNode\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "# ... (get_weather, get_coolest_cities 工具函数的定义，此处省略) ...\n",
    "\n",
    "tools = [get_weather, get_coolest_cities] #  工具列表\n",
    "\n",
    "tool_node = ToolNode(tools) # 创建 ToolNode 实例，注册工具列表\n",
    "\n",
    "model_with_tools = ChatOpenAI(model=\"Qwen/Qwen3-8B\", temperature=0).bind_tools(tools) # 绑定工具列表到 LLM 模型\n",
    "\n",
    "def should_continue(state: MessagesState): # 条件路由函数，判断是否继续工具调用\n",
    "    messages = state[\"messages\"]\n",
    "    last_message = messages[-1] # 获取最后一个消息 (LLM 模型的输出)\n",
    "    if last_message.tool_calls: # 判断最后一个消息是否包含 tool_calls (工具调用请求)\n",
    "        return \"tools\" # 如果包含 tool_calls, 则路由到 \"tools\" 节点 (ToolNode)，执行工具调用\n",
    "    return END # 如果不包含 tool_calls,  则路由到 END 节点，结束流程\n",
    "\n",
    "def call_model(state: MessagesState): #  LLM 模型节点函数\n",
    "    messages = state[\"messages\"]\n",
    "    response = model_with_tools.invoke(messages) # 调用 LLM 模型，生成 AI 消息 (可能包含 tool_calls)\n",
    "    return {\"messages\": [response]} # 返回包含 AI 消息的状态更新\n",
    "\n",
    "workflow = StateGraph(MessagesState) # 创建 StateGraph 实例，状态 Schema 为 MessagesState\n",
    "\n",
    "workflow.add_node(\"agent\", call_model) # 添加 LLM 模型节点，节点名为 \"agent\"\n",
    "workflow.add_node(\"tools\", tool_node) # 添加 ToolNode 节点，节点名为 \"tools\"\n",
    "\n",
    "workflow.add_edge(START, \"agent\") # 定义从 START 节点到 \"agent\" 节点的边 (流程入口)\n",
    "workflow.add_conditional_edges(\"agent\", should_continue, [\"tools\", END]) #  定义条件边，根据 \"agent\" 节点的输出，动态路由到 \"tools\" 节点或 END 节点\n",
    "workflow.add_edge(\"tools\", \"agent\") # 定义从 \"tools\" 节点到 \"agent\" 节点的边 (ReAct 循环)\n",
    "\n",
    "app = workflow.compile() # 编译 LangGraph 图\n",
    "\n",
    "app.invoke({\"messages\": [{\"role\": \"user\", \"content\": \"what is the weather in sf\"}]})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a2e21f3b",
   "metadata": {},
   "source": [
    "##### 示例 3-32：自定义工具调用错误处理策略 （模型降级 + 清理错误信息）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6bee9fac",
   "metadata": {},
   "outputs": [],
   "source": [
    "import json\n",
    "from typing import Literal\n",
    "\n",
    "from langchain_core.messages import AIMessage, ToolMessage\n",
    "from langchain_core.messages.modifier import RemoveMessage\n",
    "\n",
    "from langgraph.graph import MessagesState, StateGraph, END, START\n",
    "from langchain_core.tools import tool\n",
    "from langchain_openai import ChatOpenAI\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from pydantic import BaseModel, Field\n",
    "\n",
    "class HaikuRequest(BaseModel):\n",
    "    topic: list[str] = Field(\n",
    "        max_length=3,\n",
    "        min_length=3,\n",
    "    )\n",
    "\n",
    "@tool\n",
    "def master_haiku_generator(request: HaikuRequest):\n",
    "    \"\"\"Generates a haiku based on the provided topics.\"\"\"\n",
    "    model = ChatOpenAI(model=\"Qwen/Qwen2-1.5B-Instruct\", temperature=0)\n",
    "    chain = model | StrOutputParser()\n",
    "    topics = \", \".join(request.topic)\n",
    "    haiku = chain.invoke(f\"Write a haiku about {topics}\")\n",
    "    return haiku\n",
    "\n",
    "def call_tool(state: MessagesState):\n",
    "    # 创建工具名称到工具函数的映射字典\n",
    "    tools_by_name = {master_haiku_generator.name: master_haiku_generator}\n",
    "    messages = state[\"messages\"]\n",
    "    last_message = messages[-1]  # 获取最后一条消息\n",
    "    output_messages = []\n",
    "    # 遍历最后一条消息中的所有工具调用\n",
    "    for tool_call in last_message.tool_calls:\n",
    "        try:\n",
    "            # 根据工具名称找到对应的工具函数并调用，传入参数\n",
    "            tool_result = tools_by_name[tool_call[\"name\"]].invoke(tool_call[\"args\"])\n",
    "            # 将工具调用结果封装为ToolMessage添加到输出消息列表\n",
    "            output_messages.append(\n",
    "                ToolMessage(\n",
    "                    content=json.dumps(tool_result),\n",
    "                    name=tool_call[\"name\"],\n",
    "                    tool_call_id=tool_call[\"id\"],\n",
    "                )\n",
    "            )\n",
    "        except Exception as e:\n",
    "            # 如果工具调用失败，捕获异常并返回错误信息\n",
    "            # 将错误信息封装为ToolMessage，并在additional_kwargs中标记错误\n",
    "            output_messages.append(\n",
    "                ToolMessage(\n",
    "                    content=str(e), \n",
    "                    name=tool_call[\"name\"],\n",
    "                    tool_call_id=tool_call[\"id\"],\n",
    "                    additional_kwargs={\"error\": e},  # 在额外参数中存储错误对象，用于后续错误处理\n",
    "                )\n",
    "            )\n",
    "    return {\"messages\": output_messages}\n",
    "\n",
    "# 初始化基础模型（较弱的模型）\n",
    "model = ChatOpenAI(model=\"Qwen/Qwen2-1.5B-Instruct\", temperature=0)\n",
    "model_with_tools = model.bind_tools([master_haiku_generator])\n",
    "\n",
    "# 初始化更强大的模型（用于降级策略）\n",
    "better_model = ChatOpenAI(model=\"Qwen/Qwen2.5-7B-Instruct\", temperature=0)\n",
    "better_model_with_tools = better_model.bind_tools([master_haiku_generator])\n",
    "\n",
    "def should_continue(state: MessagesState):\n",
    "    # 决定是否继续工具调用循环或结束流程\n",
    "    messages = state[\"messages\"]\n",
    "    last_message = messages[-1]\n",
    "    if last_message.tool_calls:  # 如果最后一条消息包含工具调用请求\n",
    "        return \"tools\"  # 继续执行工具调用\n",
    "    return END  # 否则结束流程\n",
    "\n",
    "def should_fallback(\n",
    "    state: MessagesState,\n",
    ") -> Literal[\"agent\", \"remove_failed_tool_call_attempt\"]:\n",
    "    # 决定是否需要降级到更强大的模型\n",
    "    messages = state[\"messages\"]\n",
    "    # 查找是否有失败的工具调用消息（通过 additional_kwargs 中的 error 标记识别）\n",
    "    failed_tool_messages = [\n",
    "        msg\n",
    "        for msg in messages\n",
    "        if isinstance(msg, ToolMessage)\n",
    "        and msg.additional_kwargs.get(\"error\") is not None\n",
    "    ]\n",
    "    if failed_tool_messages:  # 如果存在失败的工具调用\n",
    "        return \"remove_failed_tool_call_attempt\"  # 路由到移除失败尝试的节点\n",
    "    return \"agent\"  # 否则继续使用当前模型\n",
    "\n",
    "def call_model(state: MessagesState):\n",
    "    # 使用基础模型处理消息\n",
    "    messages = state[\"messages\"]\n",
    "    response = model_with_tools.invoke(messages)\n",
    "    return {\"messages\": [response]}\n",
    "\n",
    "def remove_failed_tool_call_attempt(state: MessagesState):\n",
    "    # 移除失败的工具调用尝试，清理消息历史\n",
    "    messages = state[\"messages\"]\n",
    "    # 从后向前查找最近的AI消息索引\n",
    "    last_ai_message_index = next(\n",
    "        i\n",
    "        for i, msg in reversed(list(enumerate(messages)))\n",
    "        if isinstance(msg, AIMessage)\n",
    "    )\n",
    "    # 获取需要移除的消息（从最近的AI消息开始的所有消息）\n",
    "    messages_to_remove = messages[last_ai_message_index:]\n",
    "    # 返回移除指令，通过RemoveMessage标记需要移除的消息\n",
    "    return {\"messages\": [RemoveMessage(id=m.id) for m in messages_to_remove]}\n",
    "\n",
    "# 降级策略：使用更强大的模型重试\n",
    "def call_fallback_model(state: MessagesState):\n",
    "    # 使用更强大的模型处理消息\n",
    "    messages = state[\"messages\"]\n",
    "    response = better_model_with_tools.invoke(messages)\n",
    "    return {\"messages\": [response]}\n",
    "\n",
    "# 创建状态图\n",
    "workflow = StateGraph(MessagesState)\n",
    "\n",
    "# 添加节点\n",
    "workflow.add_node(\"agent\", call_model)  # 基础模型节点\n",
    "workflow.add_node(\"tools\", call_tool)  # 工具调用节点\n",
    "workflow.add_node(\"remove_failed_tool_call_attempt\", remove_failed_tool_call_attempt)  # 清理失败尝试节点\n",
    "workflow.add_node(\"fallback_agent\", call_fallback_model)  # 降级模型节点\n",
    "\n",
    "# 添加边和条件边\n",
    "workflow.add_edge(START, \"agent\")  # 流程从agent节点开始\n",
    "workflow.add_conditional_edges(\"agent\", should_continue, [\"tools\", END])  # 根据should_continue函数决定是继续工具调用还是结束\n",
    "# 根据工具调用结果决定是继续使用当前模型还是清理失败尝试\n",
    "workflow.add_conditional_edges(\"tools\", should_fallback, path_map = {\"agent\": \"agent\", \"remove_failed_tool_call_attempt\": \"remove_failed_tool_call_attempt\"}) \n",
    "workflow.add_edge(\"remove_failed_tool_call_attempt\", \"fallback_agent\")  # 清理失败尝试后使用降级模型\n",
    "workflow.add_edge(\"fallback_agent\", \"tools\")  # 降级模型生成的工具调用请求继续由tools节点处理\n",
    "\n",
    "app = workflow.compile()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2fb149d8",
   "metadata": {},
   "source": [
    "##### 示例 3-33：工具函数返回 Command 对象，更新图状态"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "eb8dfc6c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'agent': {'messages': [AIMessage(content='\\n\\n', additional_kwargs={'tool_calls': [{'id': '0198c63299bb4d6ea95454561580b240', 'function': {'arguments': ' {}', 'name': 'lookup_user_info'}, 'type': 'function', 'index': 0}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 1202, 'prompt_tokens': 159, 'total_tokens': 1361, 'completion_tokens_details': {'accepted_prediction_tokens': None, 'audio_tokens': None, 'reasoning_tokens': 1199, 'rejected_prediction_tokens': None}, 'prompt_tokens_details': None}, 'model_name': 'Qwen/Qwen3-8B', 'system_fingerprint': '', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run--305a2ece-528b-44bb-b931-77ec5d5ab535-0', tool_calls=[{'name': 'lookup_user_info', 'args': {}, 'id': '0198c63299bb4d6ea95454561580b240', 'type': 'tool_call'}], usage_metadata={'input_tokens': 159, 'output_tokens': 1202, 'total_tokens': 1361, 'input_token_details': {}, 'output_token_details': {'reasoning': 1199}})]}}\n",
      "{'tools': {'user_info': {'user_id': '1', 'name': 'Bob Dylan', 'location': 'New York, NY'}, 'messages': [ToolMessage(content='Successfully looked up user information', name='lookup_user_info', id='50ec465b-cc25-438b-9679-d68db0486b62', tool_call_id='0198c63299bb4d6ea95454561580b240')]}}\n",
      "{'agent': {'messages': [AIMessage(content='\\n\\nYou are Bob Dylan, and you live in New York, NY.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 832, 'prompt_tokens': 201, 'total_tokens': 1033, 'completion_tokens_details': {'accepted_prediction_tokens': None, 'audio_tokens': None, 'reasoning_tokens': 817, 'rejected_prediction_tokens': None}, 'prompt_tokens_details': None}, 'model_name': 'Qwen/Qwen3-8B', 'system_fingerprint': '', 'finish_reason': 'stop', 'logprobs': None}, id='run--419c7894-ddb3-410e-a37b-116f4b2fb82c-0', usage_metadata={'input_tokens': 201, 'output_tokens': 832, 'total_tokens': 1033, 'input_token_details': {}, 'output_token_details': {'reasoning': 817}})]}}\n"
     ]
    }
   ],
   "source": [
    "\n",
    "from typing_extensions import Annotated, Any\n",
    "\n",
    "from langchain_core.tools import tool, InjectedToolCallId\n",
    "from langchain_core.messages import ToolMessage\n",
    "\n",
    "from langgraph.types import Command\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "from langgraph.prebuilt.chat_agent_executor import AgentState\n",
    "from langgraph.prebuilt import ToolNode, InjectedState\n",
    "\n",
    "\n",
    "USER_INFO = [ # 定义用户信息列表 (示例数据)\n",
    "    {\"user_id\": \"1\", \"name\": \"Bob Dylan\", \"location\": \"New York, NY\"},\n",
    "    {\"user_id\": \"2\", \"name\": \"Taylor Swift\", \"location\": \"Beverly Hills, CA\"},\n",
    "]\n",
    "\n",
    "USER_ID_TO_USER_INFO = {info[\"user_id\"]: info for info in USER_INFO} #  用户 ID -> 用户信息 字典\n",
    "\n",
    "class State(AgentState): # 定义图状态结构体, 继承自 AgentState, 并添加 user_info 状态键\n",
    "    user_info: dict[str, Any]\n",
    "    user_id: str\n",
    "\n",
    "@tool\n",
    "def lookup_user_info( # 定义工具函数 lookup_user_info\n",
    "    tool_call_id: Annotated[str, InjectedToolCallId],\n",
    "    user_id: Annotated[str, InjectedState(\"user_id\")]\n",
    "):\n",
    "    \"\"\"Use this to look up user information to better assist them with their questions.\"\"\" # 工具描述信息\n",
    "    if user_id is None:\n",
    "        raise ValueError(\"Please provide user ID\")\n",
    "    if user_id not in USER_ID_TO_USER_INFO:\n",
    "        raise ValueError(f\"User '{user_id}' not found\")\n",
    "\n",
    "    user_info = USER_ID_TO_USER_INFO[user_id] # 根据 user_id 查询用户信息\n",
    "\n",
    "    return Command( # 工具函数返回 Command 对象\n",
    "        update={ # Command 对象包含状态更新指令\n",
    "            \"user_info\": user_info, # 更新 user_info 状态键，值为查询到的用户信息\n",
    "            \"messages\": [ # 更新 messages 状态键，添加 ToolMessage\n",
    "                ToolMessage(\n",
    "                    \"Successfully looked up user information\", tool_call_id=tool_call_id\n",
    "                )\n",
    "            ],\n",
    "        }\n",
    "    )\n",
    "\n",
    "# 初始化状态图\n",
    "graph = StateGraph(State)\n",
    "\n",
    "# 定义节点\n",
    "def agent_node(state: State):\n",
    "    \"\"\"智能体节点，处理用户请求\"\"\"\n",
    "    messages = state[\"messages\"]\n",
    "    user_info = state.get(\"user_info\", {})\n",
    "    \n",
    "    # 如果有用户信息，将其添加到系统消息中\n",
    "    if user_info:\n",
    "        system_message = f\"You are assisting {user_info['name']} who lives in {user_info['location']}.\"\n",
    "    else:\n",
    "        system_message = \"You are a helpful assistant.\"\n",
    "    \n",
    "    # 调用模型处理请求\n",
    "    model = ChatOpenAI(model=\"Qwen/Qwen3-8B\", temperature=0)\n",
    "    model_with_tools = model.bind_tools([lookup_user_info])\n",
    "    response = model_with_tools.invoke([{\"role\": \"system\", \"content\": system_message}] + messages)\n",
    "    return {\"messages\": [response]}\n",
    "\n",
    "def should_use_tools(state: State):\n",
    "    \"\"\"决定是否使用工具\"\"\"\n",
    "    messages = state[\"messages\"]\n",
    "    last_message = messages[-1]\n",
    "    \n",
    "    # 检查最后一条消息是否包含工具调用\n",
    "    if hasattr(last_message, \"tool_calls\") and last_message.tool_calls:\n",
    "        return \"tools\"\n",
    "    return \"end\"\n",
    "\n",
    "# 使用 ToolNode 简化工具调用逻辑\n",
    "tools_node = ToolNode([lookup_user_info])\n",
    "\n",
    "# 添加节点到图\n",
    "graph.add_node(\"agent\", agent_node)\n",
    "graph.add_node(\"tools\", tools_node)\n",
    "\n",
    "# 添加边和条件边\n",
    "graph.add_edge(START, \"agent\")\n",
    "graph.add_edge(\"tools\", \"agent\")\n",
    "graph.add_conditional_edges(\"agent\", should_use_tools, {\"tools\": \"tools\", \"end\": END})\n",
    "\n",
    "# 编译图\n",
    "agent = graph.compile()\n",
    "\n",
    "# 调用 ReAct 智能体，通过 config 参数传递运行时参数 user_id\n",
    "for chunk in agent.stream(\n",
    "    # 通过 user_id 状态键传递运行时参数\n",
    "    {\"messages\": [(\"human\", \"who am i and where do i live?\")], \"user_id\": \"1\"},\n",
    "):\n",
    "    print(chunk)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5590b732",
   "metadata": {},
   "source": [
    "##### 示例 3-34：使用 Annotated 和 InjectedState 注解传递运行时参数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d024150a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'agent': {'messages': [AIMessage(content='\\n\\n', additional_kwargs={'tool_calls': [{'id': '0198c631703ec9352308ad02606d3c1a', 'function': {'arguments': ' {}', 'name': 'lookup_user_info'}, 'type': 'function', 'index': 0}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 903, 'prompt_tokens': 159, 'total_tokens': 1062, 'completion_tokens_details': {'accepted_prediction_tokens': None, 'audio_tokens': None, 'reasoning_tokens': 900, 'rejected_prediction_tokens': None}, 'prompt_tokens_details': None}, 'model_name': 'Qwen/Qwen3-8B', 'system_fingerprint': '', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run--61dd0e73-0b62-41fc-83a9-e68e7f818025-0', tool_calls=[{'name': 'lookup_user_info', 'args': {}, 'id': '0198c631703ec9352308ad02606d3c1a', 'type': 'tool_call'}], usage_metadata={'input_tokens': 159, 'output_tokens': 903, 'total_tokens': 1062, 'input_token_details': {}, 'output_token_details': {'reasoning': 900}})]}}\n",
      "{'tools': {'user_info': {'user_id': '1', 'name': 'Bob Dylan', 'location': 'New York, NY'}, 'messages': [ToolMessage(content='Successfully looked up user information', name='lookup_user_info', id='1350ddbb-8cd9-44de-8624-96ea6f3025e1', tool_call_id='0198c631703ec9352308ad02606d3c1a')]}}\n",
      "{'agent': {'messages': [AIMessage(content=\"\\n\\nYou're a curious soul seeking answers, and you're currently in New York, NY. The city that never sleeps, where dreams are written in the stars and the streets are paved with possibilities. Keep that spirit alive, it's a rare and precious thing.\", additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 189, 'prompt_tokens': 201, 'total_tokens': 390, 'completion_tokens_details': {'accepted_prediction_tokens': None, 'audio_tokens': None, 'reasoning_tokens': 136, 'rejected_prediction_tokens': None}, 'prompt_tokens_details': None}, 'model_name': 'Qwen/Qwen3-8B', 'system_fingerprint': '', 'finish_reason': 'stop', 'logprobs': None}, id='run--18621d40-9d32-4d78-84cf-cc9d9ab9103c-0', usage_metadata={'input_tokens': 201, 'output_tokens': 189, 'total_tokens': 390, 'input_token_details': {}, 'output_token_details': {'reasoning': 136}})]}}\n"
     ]
    }
   ],
   "source": [
    "\n",
    "from typing_extensions import Annotated, Any\n",
    "\n",
    "from langchain_core.tools import tool, InjectedToolCallId\n",
    "from langchain_core.messages import ToolMessage\n",
    "\n",
    "from langgraph.types import Command\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "from langgraph.prebuilt.chat_agent_executor import AgentState\n",
    "from langgraph.prebuilt import ToolNode, InjectedState\n",
    "\n",
    "\n",
    "USER_INFO = [ #  用户信息列表 (示例数据)\n",
    "    {\"user_id\": \"1\", \"name\": \"Bob Dylan\", \"location\": \"New York, NY\"},\n",
    "    {\"user_id\": \"2\", \"name\": \"Taylor Swift\", \"location\": \"Beverly Hills, CA\"},\n",
    "]\n",
    "\n",
    "USER_ID_TO_USER_INFO = {info[\"user_id\"]: info for info in USER_INFO} #  用户 ID -> 用户信息 字典\n",
    "\n",
    "class State(AgentState): # 定义图状态结构体, 继承自 AgentState, 并添加 user_info 状态键\n",
    "    user_info: dict[str, Any]\n",
    "    user_id: str\n",
    "\n",
    "@tool\n",
    "def lookup_user_info( # 定义工具函数 lookup_user_info\n",
    "    tool_call_id: Annotated[str, InjectedToolCallId],\n",
    "    user_id: Annotated[str, InjectedState(\"user_id\")]\n",
    "):\n",
    "    \"\"\"Use this to look up user information to better assist them with their questions.\"\"\" # 工具描述信息\n",
    "    if user_id is None:\n",
    "        raise ValueError(\"Please provide user ID\")\n",
    "    if user_id not in USER_ID_TO_USER_INFO:\n",
    "        raise ValueError(f\"User '{user_id}' not found\")\n",
    "\n",
    "    user_info = USER_ID_TO_USER_INFO[user_id] # 根据 user_id 查询用户信息\n",
    "\n",
    "    return Command( # 工具函数返回 Command 对象\n",
    "        update={ # Command 对象包含状态更新指令\n",
    "            \"user_info\": user_info, # 更新 user_info 状态键，值为查询到的用户信息\n",
    "            \"messages\": [ # 更新 messages 状态键，添加 ToolMessage\n",
    "                ToolMessage(\n",
    "                    \"Successfully looked up user information\", tool_call_id=tool_call_id\n",
    "                )\n",
    "            ],\n",
    "        }\n",
    "    )\n",
    "\n",
    "# 初始化状态图\n",
    "graph = StateGraph(State)\n",
    "\n",
    "# 定义节点\n",
    "def agent_node(state: State):\n",
    "    \"\"\"智能体节点，处理用户请求\"\"\"\n",
    "    messages = state[\"messages\"]\n",
    "    user_info = state.get(\"user_info\", {})\n",
    "    \n",
    "    # 如果有用户信息，将其添加到系统消息中\n",
    "    if user_info:\n",
    "        system_message = f\"You are assisting {user_info['name']} who lives in {user_info['location']}.\"\n",
    "    else:\n",
    "        system_message = \"You are a helpful assistant.\"\n",
    "    \n",
    "    # 调用模型处理请求\n",
    "    model = ChatOpenAI(model=\"Qwen/Qwen3-8B\", temperature=0)\n",
    "    model_with_tools = model.bind_tools([lookup_user_info])\n",
    "    response = model_with_tools.invoke([{\"role\": \"system\", \"content\": system_message}] + messages)\n",
    "    return {\"messages\": [response]}\n",
    "\n",
    "def should_use_tools(state: State):\n",
    "    \"\"\"决定是否使用工具\"\"\"\n",
    "    messages = state[\"messages\"]\n",
    "    last_message = messages[-1]\n",
    "    \n",
    "    # 检查最后一条消息是否包含工具调用\n",
    "    if hasattr(last_message, \"tool_calls\") and last_message.tool_calls:\n",
    "        return \"tools\"\n",
    "    return \"end\"\n",
    "\n",
    "# 使用 ToolNode 简化工具调用逻辑\n",
    "tools_node = ToolNode([lookup_user_info])\n",
    "\n",
    "# 添加节点到图\n",
    "graph.add_node(\"agent\", agent_node)\n",
    "graph.add_node(\"tools\", tools_node)\n",
    "\n",
    "# 添加边和条件边\n",
    "graph.add_edge(START, \"agent\")\n",
    "graph.add_edge(\"tools\", \"agent\")\n",
    "graph.add_conditional_edges(\"agent\", should_use_tools, {\"tools\": \"tools\", \"end\": END})\n",
    "\n",
    "# 编译图\n",
    "agent = graph.compile()\n",
    "\n",
    "# 调用 ReAct 智能体，通过 config 参数传递运行时参数 user_id\n",
    "for chunk in agent.stream(\n",
    "    # 通过 user_id 状态键传递运行时参数\n",
    "    {\"messages\": [(\"human\", \"who am i and where do i live?\")], \"user_id\": \"1\"},\n",
    "):\n",
    "    print(chunk)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f8g4owxr8xk",
   "metadata": {},
   "source": [
    "**💡 工具调用核心要点**：\n",
    "\n",
    "#### 工具定义与注册\n",
    "- **@tool 装饰器**：将 Python 函数转换为 LangChain 工具，自动生成工具描述\n",
    "- **清晰的 docstring**：工具描述信息帮助 LLM 理解何时和如何使用工具\n",
    "- **类型提示**：为工具参数添加类型提示，提高工具调用的准确性\n",
    "\n",
    "#### ToolNode 工作机制\n",
    "- **工具调度**：根据 LLM 的工具调用请求，动态调度对应的工具函数\n",
    "- **错误处理**：自动捕获工具执行异常，将错误信息封装为 ToolMessage\n",
    "- **状态更新**：将工具执行结果添加到图状态的消息历史中\n",
    "\n",
    "#### ReAct 循环实现\n",
    "- **Agent 节点**：负责接收输入，决定是否需要调用工具\n",
    "- **Tools 节点**：执行具体的工具调用操作\n",
    "- **条件路由**：根据是否包含 tool_calls 决定流程走向\n",
    "- **循环反馈**：工具结果返回给 Agent，形成“推理-行动-观察”循环"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cfh581kwcus",
   "metadata": {},
   "source": [
    "## 3.6 图的可视化\n",
    "\n",
    "随着 LangGraph 流程变得越来越复杂，特别是当系统中引入了子图机制后，仅仅依靠代码来理解整个流程的结构和执行逻辑变得越来越困难。图的可视化成为了一种至关重要的辅助工具。\n",
    "\n",
    "通过将 LangGraph 图结构可视化地渲染成易于理解的图表，我们可以直观地把握流程的整体结构、节点之间的连接关系以及数据流动的方向。\n",
    "\n",
    "### 3.6.1 Mermaid 语法\n",
    "\n",
    "Mermaid 是一种流行的文本描述语言，用于快速创建各种类型的图表。LangGraph 内置了将 Graph 对象转换为 Mermaid 语法的功能。\n",
    "\n",
    "#### Mermaid 的优势：\n",
    "- **轻量级**：无需安装额外的依赖库，跨平台使用  \n",
    "- **易于编辑**：可以直接在浏览器或 Markdown 编辑器中查看和编辑\n",
    "- **易于分享**：文本格式便于版本控制和团队协作"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "h86f4i16z4",
   "metadata": {},
   "source": [
    "##### 示例 3-35 & 3-36：图可视化演示"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "15ohfvi2nrw",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "---\n",
      "config:\n",
      "  flowchart:\n",
      "    curve: linear\n",
      "---\n",
      "graph TD;\n",
      "\t__start__([<p>__start__</p>]):::first\n",
      "\ta(a)\n",
      "\tb(b)\n",
      "\tc(c)\n",
      "\td(d)\n",
      "\t__end__([<p>__end__</p>]):::last\n",
      "\t__start__ --> a;\n",
      "\ta --> b;\n",
      "\ta --> c;\n",
      "\tb --> d;\n",
      "\tc --> d;\n",
      "\td --> __end__;\n",
      "\tclassDef default fill:#f2f0ff,line-height:1.2\n",
      "\tclassDef first fill-opacity:0\n",
      "\tclassDef last fill:#bfb6fc\n",
      "\n"
     ]
    }
   ],
   "source": [
    "import operator\n",
    "from typing import Annotated, Any\n",
    "from typing_extensions import TypedDict\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "\n",
    "\n",
    "class State(TypedDict):\n",
    "    aggregate: Annotated[list, operator.add]\n",
    "\n",
    "def a(state: State):\n",
    "    print(f'Adding \"A\" to {state[\"aggregate\"]}')\n",
    "    return {\"aggregate\": [\"A\"]}\n",
    "\n",
    "def b(state: State):\n",
    "    print(f'Adding \"B\" to {state[\"aggregate\"]}')\n",
    "    return {\"aggregate\": [\"B\"]}\n",
    "\n",
    "def c(state: State):\n",
    "    print(f'Adding \"C\" to {state[\"aggregate\"]}')\n",
    "    return {\"aggregate\": [\"C\"]}\n",
    "\n",
    "def d(state: State):\n",
    "    print(f'Adding \"D\" to {state[\"aggregate\"]}')\n",
    "    return {\"aggregate\": [\"D\"]}\n",
    "\n",
    "builder = StateGraph(State)\n",
    "builder.add_node(a)\n",
    "builder.add_node(b)\n",
    "builder.add_node(c)\n",
    "builder.add_node(d)\n",
    "builder.add_edge(START, \"a\")\n",
    "builder.add_edge(\"a\", \"b\")\n",
    "builder.add_edge(\"a\", \"c\")\n",
    "builder.add_edge(\"b\", \"d\")\n",
    "builder.add_edge(\"c\", \"d\")\n",
    "builder.add_edge(\"d\", END)\n",
    "graph = builder.compile()\n",
    "\n",
    "print(graph.get_graph().draw_mermaid())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7372cc0f",
   "metadata": {},
   "source": [
    "##### 示例 3-37：使用 Mermaid.ink API 渲染 PNG 图片"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2bb71a29",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import Image, display\n",
    "from langchain_core.runnables.graph import MermaidDrawMethod\n",
    "\n",
    "png_bytes = graph.get_graph().draw_mermaid_png(draw_method=MermaidDrawMethod.API) #  使用 MermaidDrawMethod.API 指定使用 Mermaid.ink API 渲染\n",
    "display(Image(png_bytes)) #  在 Notebook 中显示 PNG 图片"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "63afd74b",
   "metadata": {},
   "source": [
    "##### 示例 3-38：使用 Mermaid 和 Pyppeteer 库渲染 PNG 图片"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4d2cf151",
   "metadata": {},
   "outputs": [],
   "source": [
    "import random\n",
    "from typing import Annotated, Literal\n",
    "\n",
    "from typing_extensions import TypedDict\n",
    "\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "from langgraph.graph.message import add_messages\n",
    "\n",
    "\n",
    "class State(TypedDict):\n",
    "    messages: Annotated[list, add_messages]\n",
    "\n",
    "\n",
    "class MyNode:\n",
    "    def __init__(self, name: str):\n",
    "        self.name = name\n",
    "\n",
    "    def __call__(self, state: State):\n",
    "        return {\"messages\": [(\"assistant\", f\"Called node {self.name}\")]}\n",
    "\n",
    "\n",
    "def route(state) -> Literal[\"entry_node\", \"__end__\"]:\n",
    "    if len(state[\"messages\"]) > 10:\n",
    "        return \"__end__\"\n",
    "    return \"entry_node\"\n",
    "\n",
    "\n",
    "def add_fractal_nodes(builder, current_node, level, max_level):\n",
    "    if level > max_level:\n",
    "        return\n",
    "\n",
    "    # Number of nodes to create at this level\n",
    "    num_nodes = random.randint(1, 3)  # Adjust randomness as needed\n",
    "    for i in range(num_nodes):\n",
    "        nm = [\"A\", \"B\", \"C\"][i]\n",
    "        node_name = f\"node_{current_node}_{nm}\"\n",
    "        builder.add_node(node_name, MyNode(node_name))\n",
    "        builder.add_edge(current_node, node_name)\n",
    "\n",
    "        # Recursively add more nodes\n",
    "        r = random.random()\n",
    "        if r > 0.2 and level + 1 < max_level:\n",
    "            add_fractal_nodes(builder, node_name, level + 1, max_level)\n",
    "        elif r > 0.05:\n",
    "            builder.add_conditional_edges(node_name, route, node_name)\n",
    "        else:\n",
    "            # End\n",
    "            builder.add_edge(node_name, \"__end__\")\n",
    "\n",
    "\n",
    "def build_fractal_graph(max_level: int):\n",
    "    builder = StateGraph(State)\n",
    "    entry_point = \"entry_node\"\n",
    "    builder.add_node(entry_point, MyNode(entry_point))\n",
    "    builder.add_edge(START, entry_point)\n",
    "\n",
    "    add_fractal_nodes(builder, entry_point, 1, max_level)\n",
    "\n",
    "    # Optional: set a finish point if required\n",
    "    builder.add_edge(entry_point, END)  # or any specific node\n",
    "\n",
    "    return builder.compile()\n",
    "\n",
    "\n",
    "app = build_fractal_graph(3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8d422f03",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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qKho3XnnvcrNzTM8Vle3Vw89dJ8hdFFfX6fnn39CN9/83x7VcOed9+rccy8wvDD+zjuv65FHjC8uv/LKc4awxahR4br11rskSZs3rzeELWbMOFPf//6PPKqrOxZLhH7964eUkZHtWFuy5AZt3bpR06bNlCSlpIzVxIlTDC+er137sRYu/GaP11i9+n3D2rx53h0hcjKLJUL33vsHTZw4xWm9paVFTz31sFauXOG03tx8VO+88w8tXnyty/M99dTDLtcXLVqsG2/8oeFr5MiRw/rNb35qCAtVVVn10UcrdcEFl7o836OP/q7LAMS9UjezUgAAIABJREFU996vM8+ca1j/7LNP9KtfLXP5nK4sX/4HlyGNKVOm6447fqqEhCTDY1999YUeeOAXhrDK2rUfa86c81zWBgAAAADAUMIYEQAAAADAkNbS0qK//OVRw3pOzlQ9/fQKl0ELSYqOjtV99z2iyy5bYnjsn/98WRUVpW5dPzg4RPff/4TOO+9ilx0IFi78phYvvsaw3lVnjcFisUTo0UefdwpaSFJgYKBmzZotP7///MjA1Yv9rsItJ6ur2+uyw8HMmWf1ouKBkZycpkcffd4QtJCkoKAg3Xbb3YbfI0kqLPza5fm2bStw2c3juutu1a233uXya2TkyFH69a8f0uzZ8wyPPffccjU3NxvWN29e73IESkJCsp5++rUuwwxnnjlXv/mN8f+XrpSW7tZHHxnHxlxxxdX6/e+XuwxaSMf//3v88Zdd/r4+8shv1d7e7nYNAAAAAAD4IsIWAAAAAIAh7eOPVxre3R8cHKIf//h/FRwc3O1zTSaTrr76ZsXFJRgeKyra4db177//CUOHipOdf/4il+tHjhx26xoD4cYbf6jo6Fi39p555rmGtaKiHSorK+72eZ999olhbcGCRQoKCnKvyAFmsUTo//7vLz3+Plxyyf8zrNXU7HG59/HHHzSsJSQk64orjIGbE5nNZl177a2G9YaGg3rvvX8a1l977QWX5/n975crKSm122uddtosl1/zrrz88jOGtYkTp+iaa26RyWTq9rnBwcG6/fafGNYbGg6qtrbGresDAAAAAOCrCFsAAAAAAIa0Tz75wLB2ww13aMyYGLeeHxQUpKuuusGw7mpsgivx8Yk97klISFJCQrJhvb6+zq1r9Le0tHGaM+d8t/eHhIRo4cLLDOur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f/21pIK7+gtXvejWrZtq165t1pcBuLywsDCFhYUVG0tJSVGDBg1KPX/fvn3Fjvfv36/9+/fr0UcfLfX85ORk+fv7y9/fv2ICA6iWquyMxQsvvKCNGzeW+XWBgYElyhTNmzdXkyZN5OHhUQlJAddjt9sVERFRbGn0q7FarWrVqpXCwsLUunVrtW3bVq1bt+buXgBAlbVz5061bdtWderUKfHcmDFjdOrUKd13332lPg8AqFy5ubnGY8oWAOBcPXv2VM+ePfX777/rq6++UteuXc2OBLgsT09PderUSZ06dVJkZKTOnTunHTt2GKtbHDx4sNj5hTeQLl26VFarVa+//rr69OljUnqg6gkKCrrsc7fccotq166txMREZWZmGuOXu3kmKipKaWlpiouLq/CcAKoPt5yx8PX1NcoUhYWKZs2alWjAAdWV1WrV5MmTNXr0aJ08ebLE8/Xr1ze2ASksVQQHB5uQFACAihcfH6+ZM2dq69atGjNmjMaMGVPinL59+5qQDABQGrYjBABztG3bVm3btjU7BlCl1K5dW7fddptuu+02SVJqaqo2b96sHTt2aPv27Tp9+rRxrt1u17PPPqvHHntMf/vb38yKDLiN8ePHG49PnDihpKQkHTlyRFartdh5+/fv17hx45SWlsYqpgDKrcqXLerWrau7775b7du3V3BwsJo3b86SP8A1aN68uebMmaOIiAhlZGRIKihhxMbGUkwCALil5ORkvf/++1q/fr0x9tFHH2n48OHy8fExMRkA4M+KrmZRdJULAIC5lixZonXr1kmSRowYwR35wFUEBgbqoYce0kMPPSRJ2rt3r7Zt26ZVq1YpOTlZkrRw4ULt27dPkydPVt26dc2MC7iNhg0bqmHDhqU+l5SUpLS0NEmXX/UCAK5Vld8348yZM7r99tt1//336+abb6ZoAZRBcHCwYmNjjYt4u92uiRMnGhcaAAC4i4yMDA0ePLhY0UKSunXrpqysLJNSAQAup2jZ4sKFCyYmAQAUlZKSop07d2rnzp06fvy42XGAKqddu3YaOXKkFi1apDvuuMMY37p1q4YPH659+/aZmA6oHhITE43HrOgNoLyqfNkCQPkEBwdr7ty5xh29R48eVUREhNLT001OBgBAxfH399d9991nHHfo0EELFizQ1KlTFRgYaGIyAEBpKFsAAAB3ZrVaNWXKFI0fP14eHgUf06Smpurxxx/XN998Y3I6wL0lJSUZj0NCQkxMAsAdULYAoFatWikmJkbe3t6SpMOHD2v06NHF9hAEAKCqGzNmjG688UbNmDFD//rXv3TTTTeZHQkAcBmULQAAQHUQHh6uOXPmyM/PT1LB9mkvv/yy3n77beXl5ZmcDnBPRVe2oGwBoLwoWwCQJIWFhWn27NmyWq2SCva1j4yMZGl1AECVsnv3bi1cuLDU55o2baoPP/xQvXr1cnIqAEBZUbYAAADVRefOnfXJJ58oLCzMGFuyZIkiIyNZfRioBN27d1enTp1Uv359thEBUG6ULQAYOnTooJkzZ6pmzZqSpISEBEVFRVG4AAC4vISEBP3tb3/TqFGj9N577xW7SwEAUPVQtgAAANXJDTfcoAULFujee+81xnbt2qVhw4Zp7969JiYD3M+LL76o2NhYtuwBUCEoWwAoplOnTpo+fbpRuIiPj1dUVJTOnTtncjIAAEpKTk7W888/r6FDh2rz5s2SJIfDoQ8++MDkZACA8qBsAQCuaejQoYqJiVFMTIxuu+02s+MAbqVGjRp67bXX9OKLLxrXQmlpaRo1apT+85//mJwOAACUhrIFgBK6deum6dOnGxf18fHxGjt2rOx2u8nJAAAobu3atdqwYYNx7Ovrq+joaL3yyismpgIAlBdlCwBwTUFBQbLZbLLZbAoICDA7DuCWBg8erHnz5hl/xy5cuKDJkyfrlVdeUW5ursnpAABAUZQtAJSqW7dumjp1qjw9PSVJe/bsUXR0tLKzs01OBgDAJSNGjFC9evXk7e2tJ598Ul988YVGjhwpq9VqdjQAQDlQtgAAANXZTTfdpMWLF6tDhw7G2JdffqlRo0YpLS3NxGQAAKAoyhYALqtXr15666235OFR8K1i165deuaZZ2hQAwCc7syZM6WO16pVS++8846++OILPfXUU/Lx8XFyMgBAZaBsAQAAqrt69eopNjZWQ4YMMcbi4+M1dOhQ/fzzzyYmAwAAhShbALii2267TZMnT5bFYpEkxcXFacKECRQuAABOYbfbNX/+fN1///36/PPPSz3n5ptvlr+/v5OTAQAqE2ULAACAgmuiiRMnavLkyapRo4YkKSMjQ1FRUfrkk09MTgdULVlZWXrxxRc1b948bdq0yew4ANwEZQsAV9W3b1+99tprRuFiy5YtmjBhApOeAIBKk5ubq8WLF2vQoEGaNWuWsrKy9P7778tut5sdDQDgBJQtAAAALrn77rv14YcfKjAwUJKUl5enqVOn6qWXXuLnZOAaHThwQGvXrtWcOXM0e/Zss+MAcBOULQBckwEDBui1114zjrds2aIXXnhBeXl5JqYCALijtLQ0/fWvf9U777yj9PR0Yzw0NPSy24kAANwLZQsAcE1TpkyRzWaTzWbT8uXLzY4DVCtt2rTR4sWLZbPZjLFvv/1Wjz32mI4ePWpiMqBqSEpKMh6HhoaamASAO6FsAeCaDRgwQP/85z+N4x9++EEvv/yy8vPzTUwFAHA3AQEBCggIMI6bNWumt99+WzExMWrUqJGJyQAAzkLZAgAAoCRfX1/NmjVLI0eONMaSkpI0bNgw7dixw8RkgOsrWrYICQkxMQkAd0LZAkCZPPDAA3rxxReN4++++06vvPKKHA6HiakAAO5m7Nix8vHx0TPPPKNly5bp9ttvNzsSAMCJKFsAAACUzsPDQ9HR0XrrrbdktVolSVlZWRo7dqzmz59vcjrAdSUmJhqPKVsAqCiULQCU2eDBgzVhwgTj+KuvvtKkSZNMTAQAqIp27typjz/+uNTnunfvrs8//1wjRowo9oEbAKB6oGwBAABwZXfeeacWLVqkZs2aSZIcDodmzZql8ePH6/z58yanA1zPgQMHjMeULQBUFMoWAK7L8OHDFRUVZRyvXLlSb7zxhomJAABVRXx8vKKjozVmzBhNnz5dqamppZ7n7+/v5GQAAFdB2QIAAODqgoODtWjRIvXu3dsY27hxo8LDw5WcnGxiMsD1PPvss4qIiNBdd92lpk2bmh0HgJugbAHgukVERBTbH3DZsmUULgAAl5VrL3glAAAgAElEQVScnKyJEycqPDxcW7dulSTl5+crNjbW5GQAAFdD2QIAAODa+Pj4aNq0aYqKipLFYpEkHTx4UOHh4frxxx9NTge4jr59+yoqKkpvvvmm2VEAuBHKFgDKJTo6WsOGDTOOly1bpnfeecfERAAAV/XFF19o/fr1xcZuvfVWDR061KREAABXRdkCAFzTxIkTFRcXp7i4OD388MNmxwFQREREhGbMmCEfHx9Jkt1u17PPPqtZs2bJ4XCYnA4AAPdE2QJAuT333HMaPHiwcbx48WLNmTPHxEQAAFc0atQoeXt7S5I6duyoBQsWaPr06WrVqpXJyQAAroayBQAAQNn17NlTixcvVkhIiDE2f/58jRs3TpmZmSYmAwDAPVG2AFAhXnzxRd1///3G8bx58/TJJ5+YmAgAYJYzZ86UOu7j46Px48frvffe0wcffKCbbrrJyckAAFUFZQsAAIDr07hxYy1cuFB33HGHMbZ161YNGzZMiYmJJiYDAMD9ULYAUGH++c9/6p577jGOp06dSuECAKqRvLw8/fvf/9aDDz6ob775ptRzHn74YfXs2dPJyQAAVQ1lCwAAgOtntVo1ZcoUjR8/Xp6enpKk1NRUPfbYY5f9eR0AAJQdZQsAFcZisei1117TXXfdZYxNnTpVy5YtMzEVAMAZtm/frkceeURvvvmmTp8+rblz5yo/P9/sWACAKqywcEHZAgAA4PqEh4crJiZGfn5+kqTs7Gy9/PLLmjp1qvLy8kxOBzjHmTNn1LdvX0VGRmr27NlmxwHgZihbAKhQHh4emjx5crHCxRtvvKEVK1aYmAoAUFkyMjL07LPPauzYsTp48KAxnpmZqSNHjpiYDABQ1RVd3YIPAwAAAK5P586d9cknnygsLMwY++STTxQZGamMjAwTkwHOkZiYqIyMDP3000/avn272XEAuBnKFgAqnKenpyZPnqy//OUvxtikSZO0Zs0aE1MBACqDr6+vEhISjOO6desqOjpaK1asULNmzUxMBgCo6thKBABcz5QpU2Sz2WSz2bR8+XKz4wC4RjfccIMWLFige++91xjbtWuXhg8frr1795qYDKh8iYmJxuPQ0FATkwBwR5QtAFQKT09PvfXWW+rZs6cx9sorr2jt2rUmpgIAVDRPT09NmDBB3t7eioiI0MqVKzVy5EhZrVazowEAqjjKFgAAABWnRo0aeu211/TSSy8Z11nHjx/XqFGjtHr1apPTAZUnKSnJeNyyZUsTkwBwR5QtAFQaLy8vTZ06VTabTZLkcDj08ssv6/vvvzc5GQCgrDIzMxUXF1fqc3fccYdWrFihqKgo+fj4ODkZAMBdUbYAAACoeA8//LAWLFiggIAASQXXWa+++qomT56s3Nxck9MBFa9o2YKVLQBUNMoWACpVjRo1NH36dHXq1EmSlJ+frxdeeIHCBQBUEXa7XfPnz9egQYP03HPP6fTp06WeV79+fScnAwC4O8oWAAAAlaNdu3ZavHixMWcrSf/5z380atQopaWlmZgMqHhFyxYhISEmJgHgjihbAKh0tWrV0syZM42L97y8PL3wwgvavHmzyckAAJeTm5urxYsXa9CgQZo1a5aysrJ09uxZzZs3z+xoAIBqgrIFAABA5alXr57mzJmjoUOHGmPx8fEaOnSodu/ebWIyoGL9+9//VkxMjJ5//nk1atTI7DgA3AxlCwBOYbVaNXPmTHXo0EFSQeFiwoQJ2rFjh8nJAAClmTp1qt555x2lp6cbY3/5y1/017/+1cRUAIDqhLIFAABA5fL09NTzzz+vyZMnq0aNGpKkjIwMRUREaMmSJSanAyqGn5+fbDZbsWIRAFQUyhYAnMZqtWrWrFkKCwuTVDBh+swzz2jXrl0mJwMA/NmIESPk6ekpSerYsaMWLlyoqVOnqlWrViYnAwBUF5QtAMD1TJw4UXFxcYqLi9PDDz9sdhwAFeTuu+/Whx9+qMDAQEkFN8q9/fbbeumll5STk2NyOgAAXBdlCwBOVbt2bcXExBgf1uXk5Cg6Olp79uwxORkAoKgmTZpowoQJmjlzpj744AO1b9/e7EgAgGqGsgUAAIDztGnTRosXL5bNZjPGvv32Wz3++OM6evSoickAAHBdlC0AOJ2Pj4/mzJmj4OBgSZLdbtdTTz1F4QIAnGzXrl0aOXKk/vvf/5b6/JAhQ9SjRw8npwIAoABlCwAAAOfy9fXVrFmzNHLkSGNs//79GjZsGNtBAwBQCsoWAEzh5+enefPmqVmzZpKk8+fP66mnnlJ8fLzJyQDA/cXHxys6OloRERHas2eP3nzzTbMjAQBQAmULAAAA5/Pw8FB0dLTeffddWa1WSVJWVpbGjh2rf/3rXyanA8pm//79ZkcA4OYoWwAwTb169RQbG1uscBEVFaWEhASTkwGAe0pPT9eLL76o8PBwbd261Rg/duyYjhw5YmIyAABKomwBAABgnj59+mjRokVq0aKFJMnhcGjmzJmaOHGizp8/b3I64OpOnjypYcOGyWazKSoqyuw4ANwUZQsApgoICFBsbKwaN24sqaAlPWbMGCUnJ5ucDADcj4+Pj37++Wfj2MvLS+Hh4VqxYoWaNGliYjIAAEqibAEAriclJUVxcXGKi4tTWlqa2XEAVLLg4GAtWrRIvXv3NsbWr1+v8PBwHT582MRkwNUlJiYaj/Pz801MAsCdUbYAYLrCwkWjRo0kSWfOnFFERIRSUlJMTgYA7qVGjRp64oknJEm33367li1bpvHjx6tOnTomJwMAoCTKFgDgepYsWaKoqChFRUXp+++/NzsOACfw9vbWtGnT9NRTT8lisUiSDh48qEcffVQ//vijyemAy0tKSjIeh4SEmJgEgDujbAHAJdxwww2KjY1VgwYNJEkZGRmKiIhgWXsAuA6ZmZnFVrAoasiQIYqJidHbb79tbOMEAIAromwBAADgOp588knNmDFDPj4+kqSzZ8/q2WefVUxMjBwOh8npgJIoWwBwBsoWAFxGkyZNFBsbK39/f0kFe6pRuACAa2e32xUbG6tBgwbp+eefV3Z2dqnn2Ww2JycDAKDsKFsAAAC4lp49e2rx4sXFPriOjY3VuHHjlJWVZWIyoCTKFgCcgbIFAJcSFBRUrHBx/PhxRURE6NixYyYnAwDXlZOTo48//lgDBw5UTEyMsrKydPr0af373/82OxoAANeNsgUAAIDrady4sRYuXKh+/foZY1u3blV4eLgSExNNTAYU5+npaTwODQ01MQkAd0bZAoDLCQ4OVmxsrOrWrSvpUuEiLS3N5GQA4JreeOMNvfvuu8rIyDDG/P39Vb9+fRNTAQBQPpQtAAAAXJPVatXrr7+uCRMmGB9oHz58WI899pjWrVtncjqgwJw5cxQXF6dPPvlE9erVMzsOADdF2QKASwoODtbcuXONPQCPHj2qiIgIpaenm5wMAFzPiBEjjMd169ZVdHS0Vq1apXvvvdfEVAAAlA9lCwBwPUFBQerSpYu6dOmiRo0amR0HgMmGDx+umJgYY5Xi7OxsvfDCC5o2bZry8vJMTgcUaNOmjdkRALgxyhYAXFarVq0UExMjb29vSQXt6NGjR+v06dMmJwMA19KyZUsNGDBAERERWrlypUaOHCmr1Wp2LAAAyoWyBQC4nqFDh2ru3LmaO3eu+vTpY3YcAC6gc+fOWrJkicLCwoyxRYsWKTIystgKnAAAuCPKFgBcWlhYmGbPnm18aJicnKzIyEgKFwCqnczMTL333ns6ceJEqc9PmjRJUVFRxopAAABUdZQtAAAAqoaAgAAtWLCg2Aqbu3bt0vDhw7Vv3z4TkwEAULkoWwBweR06dNDMmTNVs2ZNSVJCQoIiIyOVlZVlcjIAqHx2u10ffPCBBg0apA8//FDvv/++2ZEAAHAKyhYAAABVR40aNfTaa6/pH//4h3Edd/z4cT3++ONavXq1yekAAKgclC0AVAmdOnXS9OnTixUuoqKidO7cOZOTAUDl+fTTTzVw4EDNnj3bKJitWrVKqampJicDAKDyUbYAAACoeh588EEtWLBAAQEBkqTc3Fy9+uqreuONN7img9MsWbJEcXFxrJANoNJRtgBQZXTr1k3Tp083Jl3j4+M1duxY2e12k5MBQMU7deqUZsyYUWx/0969e2vp0qUKDAw0MRkAAM5B2QIAAKBqateunRYvXqxOnToZY8uWLVNERITS0tJMTIbq4MiRI3r77bcVFRWloUOHmh0HgJujbAGgSunWrZumTp0qT09PSdKePXsUHR2t7Oxsk5MBQMWqX7++Ro0aJUnq2LGjFi5cqGnTpikkJMTkZAAAOAdlCwBwPSkpKYqLi1NcXBwfmAK4onr16mnOnDnFPuzes2ePhg4dqt27d5uYDO4uISHBeBwaGmpiEgDVAWULAFVOr1699NZbb8nDo+Bb2K5du/TMM88oNzfX5GQAcH1OnTpV6nh4eLhmzJihDz74QO3bt3dyKgAAzEXZAgBcz5IlSxQVFaWoqCh9//33ZscB4OI8PT31/PPPa/LkyapVq5YkKSMjQ6NHj9bSpUtNTgd3lZiYaDxu1aqViUkAVAeULQBUSbfddpsmT54si8UiSYqLi9OECRMoXACoUnbt2qWRI0cqKipK+fn5JZ63Wq3q1auXCckAADAfZQsAAAD3cPfdd2vhwoXGtqgXLlzQW2+9pVdeeUU5OTkmp4O7KbqyBWULAJWNsgWAKqtv37567bXXjOMtW7ZowoQJ1zURa7PZKjIaAFxRfHy8oqOjFRERoT179igpKUkrVqwwOxYAAC6FsgUAAID7CA0N1eLFi4vNw3755Zd6/PHHdezYMROTwd2wsgUAZ/K6+ikA4LoGDBignJwcTZo0SVJB4eKFF17QlClT5OnpeU3vUXiBb7PZFBcXV2lZAUCS3n//fS1YsKDYmL+/v7y9vU1KBACA811r2dnhcMhisWjhwoX66KOPSn2u8H8LcU0PAADgmnx9fTVr1izNnj3bmBvZv3+/hg8frjfeeEPdunW77vdmbheFnn76af32229KSkpSWFiY2XEAuDlWtgBQ5T3wwAP65z//aRz/8MMPevnll0tdkv/PunbtKofDYRy/+eablZIRAAr16NHDeOzn56e//e1vWrVqle655x4TUwEA4DyFRQuHw2Fcixc+/vOvwudKO/9yzwEAAMB1eXh46Omnn9a7774rq9UqSTp9+rSefvppLVy48Lres/D6snv37hWWE1VXnz59FBkZyVw/AKegbAHALTzwwAN68cUXjePvvvtOr7zyyhUnXLt27SpJxt1wkrR+/XomaQFUqq5du+quu+7SmDFj9MUXX+ixxx4zJhcAAKgOiq5AV7gihcViueZfpZ1fOFZ06xEAQMULCgpSly5d1KVLFzVq1MjsOACqsD59+mjRokVq0aKFJCk/P18zZszQxIkTZbfbr/l9unXrZhRv8/Ly9Oijj1ZWZAAASqBsAcBtDB48WBMmTDCOv/rqK02aNKnU8kTXrl1LTMxKUlpamn755RfnBAbgtjIzMzVt2jSlp6eX+vybb76pMWPGqHbt2k5OBgCA+QYMGFBp733fffdV2nsDAKShQ4dq7ty5mjt3rvr06WN2HABVXHBwsBYtWqTevXsbY+vXr9djjz2mw4cPX/X1NptN+fn5xbaU+/3339lOBADgNJQtALiV4cOHKzIy0jheuXKlJk2aVOycwhUt/qywlLFhw4bKCwjArdntdsXGxmrQoEFatGiRPvzwQ7MjAQDgcsaOHSsvL69iK8yVh8PhMFa1GD16dAUkBAAAgLN4e3tr2rRpGjt2rFGaSEpK0qOPPqotW7Zc8bV/vpYsPP74448rJywAAH9C2QKA2xk9erRGjhxpHK9cuVJvvPGGpOJbh1zO2rVrKzcgALeTk5Ojjz/+WAMHDlRMTIyysrIkSUuXLlVGRobJ6QAAcC2NGjXS0KFDJanchYuir+3bt68CAwPLnQ8AAADO98QTT2jWrFny8fGRJJ09e1bjxo3T3LlzS71etNlskkpuSydJP/744zWtjAEAQHlRtgDglqKjozVs2DDjeNmyZbLZbMUuuv+scDw1NVXx8fFOyQnAPZw5c0bvvfdesWJFgwYNNH78eGOSAAAAXPLkk0/K29tb0pWL0Nei8PVFC9cAAACoerp166bFixcrJCTEGJs7d67GjRtn3NgilSxaFCpayoiNja3ktHBFf//73xUTE8Pq1QCchrIFALf13HPPafDgwZJKLilXGofDwVYiAK5LQECAHnzwQUlS3bp1NW7cOK1YsUKPPPKIvLy8TE4HAIDr8fX11SOPPGIcX8/qFkVfY7PZ1KpVqwrJBgAAAPM0btxYCxcuVL9+/YyxrVu3Kjw8XMnJybLZbMXmcYsqWr5Ys2YNq1tUMwcPHtR3332n2NhYTZ061ew4AKoJyhYA3NqyZcskXdvdckVXvVi/fn2l5gJQdaWnp5c6/uSTT+rJJ5/UypUr9fjjj8tqtTo5GQAAVcuIESNUp06dcr0Hq1oAgHOlpKQoLi5OcXFxSktLMzsOADdltVr1+uuv67nnnpOnp6ck6fDhwwoPD5fD4bji6sVSQSk3Ly9PH374obMiwwUkJCQYj0NDQ01MAqA6oWwBwG0VtpzLovD8xMREpaSkVEYsAFXUrl27NGLECI0fP77U5wMCAvTUU0+xbQgAANfI399fo0aNMo7Lcu1e9Ny2bduqR48eFZoNAFC6JUuWKCoqSlFRUfr+++/NjgPAzQ0bNkwxMTHy9/eXJJ0/f75Mr1+1alWxLV/h3hITE43HrHoHwFkoWwBwS9dTtChU+LrvvvuuIiMBqKLi4+M1duxYRUREaO/evfr111+1adMms2MBAOAWhg0bpkaNGl3TSnRFFb2b8amnnqqMaAAAAHABnTt31pIlS4zja13B2OFwKDc311j5GO6PlS0AmIGyBQC3U7RocT2TtoXWrl1bobkAVD0zZsxQeHi4tm/fXmx87969JiUCAMC91KpVS08//bSkS5Pi16LwvDZt2qh3796Vlg8AAADmu+eee4ztQ8pq6dKlysnJqYRUcDWsbAHADJQtALiV8hQt/iw+Pl6pqakVEQtAFdW2bdtix71799bSpUsVERFhUiIAANzPgAEDik2GXq1wUXSi/fHHH6/UbAAAADBXeVYwlqRTp05p9erVFZgIrupf//qX5syZoxdeeIGyBQCnoWwBwG1UZNGi8H3WrVtX7lwAqq7+/fsrODhYHTt21MKFCzVt2jSFhISYHQsAALfi4eFhFBmLbg9yNYGBgerXr19lRgMAAIDJyrOCceFr58+frwsXLlR4NrgWX19fde3aVY888ojZUQBUI15mBwCAilCRRYuir9+wYYOGDx9ervcD4Pp++OEH/eUvfyn1ublz56p+/fpOTgQAQPVy5513qmXLljpw4MAVr+uLrmoxevRoeXhwDwkAOFOvXr1Ut25dSVL79u1NTgPA3dlsNknln+89evSovvzyS91///0VEQsAAAOzEgDcRuFFt8PhKNfScoXvIUk7d+5URkZGubMBcE27du3SiBEjNGHCBC1atKjUcyhaAABQ+Tw8PPTMM89IurbJ9CZNmmjgwIGVHQsA8Ce33nqrIiMjFRkZqXbt2pkdB4AbK7y5rjxFi6KrW3z00UcVFQ0AAANlCwBuIS4uTlLxZYcronQhSevXry/3ewBwLfHx8Xr66acVERGhvXv3SpLmzZun06dPm5wMAIDqq3fv3uratatx/Odr+aKT7eHh4fL09HRqPgAAADhHRa1oUdSBAwe0efPmCns/AAAkyhYA3EhcXJwGDRqkOnXqyMPDo9TixbWWL4q2nilbAO7l8OHDCg8P17Zt24yxBg0aaNy4cfLz8zMxGQAAGDNmjKQrT6z7+vqyBDQAAICbKlzRoiJuoitU+F7z5s2rsPcEAECibAHAzbz66qv67rvv9O677+q+++6Tj4+PpJIrXpTF1q1blZWVVeFZAZijWbNm6tu3ryTJz89PzzzzjL744gsNHjzY5GQAAKBr1666+eabJRUvXBRd1WLIkCGyWq2m5AMAAEDlK5zLLXoD3fUWMIpeU+7evVtbtmypyKhwEXfddZciIiL0xhtvmB0FQDXjZXYAAKhoXl5e6t27t3r37q0LFy5o69at+vrrr7Vu3TplZ2cb5/354vzPd88VHufn5+uHH37QvffeW/nhATjFuHHj1LJlS40YMUK1a9c2Ow4AAChiwoQJGjlypCSV2Kfby8tLQ4YMMSsaAAAAKllcXNxltxEpLFwUXZW4tPNKU/i6jz76SD179qzY0DBVQkKCTp8+rV27dik9Pd3sOACqGVa2AODWCosXkyZN0po1a/TUU08pMDBQ0qWG9LVsNbJu3Tqn5gZQPpmZmZoxY4ZeeeWVUp9v2rSpIiMjKVoAAOCCOnTooLvuuss4Lnp9/uCDD6pevXpmxAIASNq0aZPmzJmjOXPmaO/evWbHAeCm4uLi9OSTT+qWW24xVi6WZGwdLRVfyVi6dM1Y2txu0fO2b9+u5OTkyooOEyQkJBiPW7VqZWISANWRxVGRG1850fjx47Vx40ZJ0syZM9WjRw+TE+F6ZWbkauPqVKUknFfehXzl50s52flSlfyTCQCAe3A4JA9PqZbVQ/n5Ut36NWS7rYHa2fzNjgZc1vbvTmj/L5nKTM9RvqSc83nKd0gWXf0uJwAAUDlqeXvIYpF8/LzUJLiO7hrc2OxIKIcpU6bo008/lSS99NJLevjhh01OhOpmw4pUHfgtU/ZzeZJDys7OlyPf7FQAAFRvDkm1rAUluPx8qWU7H906oJH8A2qaHa3SsY0ITLNr0yn9/MNJZaTlyiExBQ4AgAuxWCRHvmQ/VzBrdeJIttYs/kPfLD2iNh3r6vb7A+Xty6UkzHfyaLZ+WHVMB3/PUskaOTULAADMln2+4Hry/LkcpR3N0X+3pqtZSB31GXiDAptbTU4HoCpISTirjauPK/XQ+YsjzCYDAOBKLJJy7A4V3kn/+67T2rfrjHz8PXVTj/rq0a+hqfkqEzPkcLrUg3Ytn5es7HP5kqXgLyCXxgAAuLaLi3QqP0+K33lG8T+fUedb6+v2BwNNTobqbMUHh5T0W5bZMQAAwDUw5n4c0uHEs/pkeqKahdTRQ2NayKsGM0MASjp7+oKWz0lW2rFsWRyWIt9I+J4BAIArK7z9KSsjT5vXnFDc+pN6MKK5moa437belC3gVOs/S9WujSclWVRkmzTVqOWhgMZWNQ31Vaub/FSztqdpGQEAwCUp+zOVsi9TqQfPKSsjR8aklkPaufGk9v33tIY9Eypffy4r4Typyee1bO5B5Wb/ab1gi+TfoKYah9RRcJifGjbzNicgAAAwnE7LVuLu0zqSlKX0E9nGcv8WWfRH0jnN/me87nyosW68he3qAFzy244Mfbv0iPLyL35gc/FHUQ9Pi+o1qqVmoT4KuclfvvVqmBsUAABIki7Y85S4J0Mp+88q7ei5iytdFKygnJudr6UzD6hDj3rq90gTk5NWLGbF4TQr5h9S0q9ZKto89q7jpR73Biqota95wQAAwGUFtfY1/p3Osedp48qjOpxwRgXTXRadPZOnhVMSFD4hVH4NmORC5Tu0/5w+iz2o/AuX9gyxeEitOvmr5z3sAQ8AgKvxC6ilLnc0Upc7GkmSdq47rt92nFJ+XsG/5RdyHfp26RF51bSobSc/M6MCcBG/bE7Xuv8ckRyXtgX0qmFRh54Burl3gKnZAABA6bysnmpra6C2tgaSpKMHzmrTqiM6l3mh4ASLRXu2Zch+Nk+DRgWZmLRiUbaAU3y56PDFokUBi0XqeW+gWnWsZ2IqAABQFjWtnrrzkWbKsedp1QdJyjpdcKGcY8/XwrcT9ORLrVW7LpeXqDxpR+z6bG7yxbvbCgS2qK27H21hai4AAHDtutzZSF3ubKQN//lDh+LPSJIc+dKXi/5QjZoeCmnPDTmurFevXqpbt64kqX379iangTv6ZdMprftPqhy6VLRo09lfPQZQrAYAoCpp3LKOBo9rrQO/ntbGL44YK9zt352pz2MP6a8Rzc0NWEE8zA4A9/fr9gz9/vMZ47hGTYv+zzNtKFoAAFBF1bR66qGnW6vVzZfuPLyQ49CymGQTU6E6+HfMQeVfLFo4HJLtrhsoWgAAUEXd/lBT9Xng4hLCFkkO6YsPU3TuTK6puXBlt956qyIjIxUZGal27dqZHQdu5mRqtjasSJVU8G3BYpH6DWtO0QIAgCqs5Y1+Gja+jWpaPSUV/PuetDdTuzaeMjlZxaBsgUp15lSuvl16xDiuabXo4ejWstb2NDEVAACoCL0GNlE726Xy5MljOfp68R8mJoI7+2Rqkuxn8yQ5JIdDve4LVPvu9c2OBQAAyqHljX664/80u3jkUP4F6ePpSaZmAmCeT2ceUH6e5JBDFg+pf3iwGresY3YsAABQTl5WTw2d0Ea1ansW/Dsvi9Z/nqpjh+1mRys3yhaoVKs/OiyHsZ22Q/c+HmI0lwAAQNXX7e5A1W9UyzjeG5ehzAzuRkTFOrjvbJEfviwK7eiv1p1YJQ0AAHcQ1NpXnfo0VOEmYZkZF7R7a4a5oQA43eY1x5V9vmB9cYss6jkgUI2CvE1OBQAAKtIDEaGyWC5uFOaQvl1S9W/co2yBSnP8sF1HD503jjv2bqi6DWqamAgAAFSGe8JbyOPiVaXDIa1detTcQHA7Xy/5Qw5LQYO3preHbh3YxOREAACgIt3cJ0B169eQVFC5+H5lqrmBADjdjvVpKrxrL6BJLbagBgDADVl9PGW78wbj+MTRbP2RdM7EROVH2QKVZv1nRy9uuelQLW8PdfxLQ7MjAQCASuBl9dTNvS/+O2+xKCXxrLmB4FZSU+w6m5Ery8W7XW9/qNlVXgEAAKqiOwYHXXxkUW52nvbGnTY1DwDn2bj6mPIuqHl64tgAACAASURBVKBtZXHozkdamB0JAABUkvbd68vqU7ALgsMh/fBF1S5aU7ZApTn+R8FSzxZZ1M7WwOQ0AACgMt3cO6Bw5WddyHUobn2auYHgNor+Warl7anAFuzZDACAO/ILqCXfejUuHlm0Z3u6qXlQuk2bNmnOnDmaM2eO9u7da3YcuInEPWdkkUOSRQGNrbLWZhtqAADcWcdeBZ8bWyzSiSP2q5zt2ihboFL8kXReF3LzjeOb+wSYmAYAADhDo6YX99O1OJS4J8vcMHAbhxOzCn7ykhRyU12T0wAAgMrUvvulm3WOpZy/wpkwy+bNmzVv3jzNmzdPv/32m9lx4CYyTuYa1/ydb7/hKmcDAICqrm2Rm/TzLjiUsCfTxDTlQ9kClSJ+Z4YKb2+tXZcmMgAA1UHozX6SCla1On6EyXFUjPNZlwq8N3ZntTQAANxZ2y71CtYSlpSb41Bmeq7JiQBUtgPxWcrPu3hgkRoHs5IdAADVQd0iq9rt+7nqbiFI2QKVougHLH4NrCYmAQAAztIs1Nd4nJuTf4UzgWuTevDSNaXFItX2rXGFswEAgDvwqnlpuvJw4jkTkwBwhmMHL/09r2Xl4woAAKoL/xtqGY9PHcs2MUn5cPWCSuHIsxTeiCAfPybFAQCoDrx9vYw7ES2S8nId5gZClZeelmM89qzBjy4AAFQHRcsWZzMvmJgEgDPYz+fJoYKfHa21vUxOAwAAnMW38PNjh0N5eVX3xj1mLFEpzp3NLdxmTzVrWcwNAwAAnMbiUfjvvkVZZ5gcR/lk2y/9oOXhQXkHAIDqwNPr0jxS1hm2EQHc3dkzebJc3I666N9/AADg3mp6XyxZWiyyn6u6ZQuqoqgcjksXxlX3rwcAACiP/CrcSIaLyL/0Z8ghJl4BAKgOLEX/zc8zLwdK16tXL9WtW1eS1L59e5PTwB3kOy6Vqi1c8gMAUG0UuQSQw1F1b7KibAEAAAAAAAAAuKpbb71Vt956q9kxAAAAAJfANiIAAAAAAAAAAAAAAABlQNkCAAAAAAAAAAAAAACgDChbAAAAAAAAAAAAAAAAlAFlCwAAAAAAAAAAAAAAgDLwMjsAAAAAAAAAAMD1LVmyROvWrZMkjRgxQn369DE5EQAAAGAeyhYAAAAAAAAAgKtKSUnRzp07JUn9+/c3OQ0AAABgLrYRAQAAAAAAAAAAAAAAKAPKFgAAAAAAAAAAAAAAAGVA2QIAAAAAAAAAAAAAAKAMKFsAAAAAAAAAAAAAAACUgZfZAQAAAAAAAAAArm/o0KG64447JEnBwcEmpwEAAADMRdkCAAAAAAAAAHBVQUFBCgoKMjsGAAAA4BLYRgQAAAAAAAAAAAAAAKAMKFsAAAAAAAAAAAAAAACUAWULAAAAAAAAAAAAAACAMqBsAQAAAAAAAAAAAAAAUAZeZgcAAAAAAAAAALi+JUuWaN26dZKkESNGqE+fPiYnAgAAAMxD2QIAAAAAAAAAcFUpKSnauXOnJKl///4mpwEAAADMxTYiAAAAAAAAAAAAAAAAZUDZAgAAAAAAAAAAAAAAoAwoWwAAAAAAAAAAAAAAAJQBZQsAAAAAAAAAAAAAAIAy8DI7AAAAAAAAAADA9Q0dOlR33HGHJCk4ONjkNAAAAIC5KFsAAAAAAAAAAK4qKChIQUFBZscAAAAAXALbiAAAAAAAAAAAAAAAAJQBZQsAAAAAAAAAAAAAAIAyoGwBAAAAAAAAAAAAAABQBpQtAAAAAAAAAAAAAAAAyoCyBQAAAAAAAADgqqZMmSKbzSabzably5ebHQcAAAAwFWULAAAAAAAAAAAAAACAMqBsAQAAAAAAAAAAAAAAUAaULQAAAAAAAAAAAAAAAMqAsgUAAAAAAAAAAAAAAEAZULYAAAAAAAAAAAAAAAAoAy+zAwAAAAAAAAAAXN/EiRM1ceJEs2MAAAAALoGVLQAAAAAAAAAAAAAAAMqAsgUAAAAAAAAAAAAAAEAZULYAAAAAAAAAAAAAAAAoA8oWAAAAAAAAAAAAAAAAZUDZAgAAAAAAAAAAAAAAoAwoWwAAAAAAAAAArmrKlCmy2Wyy2Wxavny52XEAAAAAU1G2AAAAAAAAAAAAAAAAKAPKFgAAAAAAAAAAAAAAAGXgZXYAAK7vwIEErVpVfGnIbt16qUePPiYlAuCKPv30Xzp+/Jhx7OXlpSeeiFatWrVMTOU68vLylJubKy8vL3l5cQkGoPqx2+2aP3+m8vLyjLHGjZtq8OBwE1MBcDX8/AkAQNXFNT+Aa7F58wb99NO2YmN//esQBQUFm5TINZ07d05nzmTIw8NDNWvWktXqLavVanYs/Akz/QCu6uDBJK1atazYWM2aNZnsAlDMmjWf6+jRP4qNDR/+JGWLi+bPn6llyxZp4MDBGjfu72bHAQCny8o6oxUrPi021rx5SyZeARRTXX/+3L59k7Zt26jz58/p/Plzys62y24/r5ycbGVnZysvL0/5+XmqUaOmmjZtrqZNg9SkSTM1bx6iDh06yWKxmP0lAADANT+Aa/LTT9tKXPN37dq92pctDhxI0MaN67Rp03qlph7R+fPnSpwTENBIbdq0V0hIa3Xr1kthYR1MSIqiKFsAAABUMrvdrtWr/yNJ+u67LzV69DO0kAEAAGD4/fc9JSacL+fQoQPFjlu3DlN4+Gh1796H0gUAAABQxSQm7tO0af+/9u377arnpqUdV1racW3evEGLFs1T69ZhevLJcerc+RYnJEVpPMwOAAAwz6lTJ3X8eKrZMQC3t3nzBqOJfP78OW3fvtHkRAAAVJz8/Hzt3x9fbLloAM6zf3+8Xn31OY0f/4TOnTtrdhy4uYkTJyouLk5xcXF6+OGHzY4DAHASu92u5OREs2MAbuXChQuaP3+mxo599JqKFqXZvz9ecXFbKjgZyoKyBQBUMxcuXNC2bRs1adLfNWzYPVq4cI7ZkQC3t3p18X3H165dbVISAAAqzvHjqVq8eL5GjBik6OgROnbsqNmRgGotPn6P3n33f+VwOMyOAgAA3MS+fb/p/fenaOjQ/vr7358yOw7gNi5cuKC33/7/9OmnH5b7vW67rV8FJML1YhsRAKhG9u+P1//9v39TRka62VGAauPgwSTt2bOr2Ni2bRt18uQJNWjQ0KRUAACUz7Rpk7VmzWdmxwDcmrd3bfXufadq1bLKarXKw8NDJ0+m6dixIyWuLwv98MNatW17owYPDndyWgAA4E7sdrvGjXus2PZltWrVMjER4D4Kixbr1391xfM6drSpUaNA5efnG9uH/PHHoWLntGzZSm3atK/MuLgKyhYAUI1kZJyiaAE42ddfryx1fMOGb/Tww486OQ0AABUjMfF3syMAbq937zv1/POvlvrcyZMntHz5x1q+/OMSzy1aNI+yBQAAKJf8/LxiRQsAFefzzxdftmjh719PY8aMV8+et6t27dolns/Pz9fPP2/XihVLtW3bjxo4kG3dzEbZAgAAoJLY7XZ9+WXpd/1+9dUKyhYAAAC4Lg0aNNSYMeMlqUTh4vz5czp+PFWNGgWaEQ0AAADAZRw6dEDz5s0o9blevW7XhAn/lK9v3cu+3sPDQ1279lDXrj104sQx+fr6VVZUXCMPswMAAAC4q82bN+j8+XOlPnfo0AHt3x/v5EQAAABwJyNHji11/MCBBCcnAQAAAHA177zzWqnjXbv20D/+8foVixZ/1rDhDbJarRUVDdeJsgUAAEAlWbVq2RWfX7dujZOSAAAAwB3VrFlTrVuHlRg/dCjJhDSoDqZMmSKbzSabzably5ebHQcAAKDK2L37Z8XH7ykx7u9fT//4x+vy8mJDiqqI/9eAy4iP32PcCWKxWNSv30B5enpKkvLy8vTrr79o06b1SklJ1okTx5SXl6cWLUIUGtpGzZu3lM3Wq9T9lK7F+fPn9csvO7RjxxYdPXpYJ04cU25ujnx9/VSvXgOFhrZWly491K7dTeX65pufn298HYcOHVBq6hHl5+epWbMWF38112233X3d719USkqyduzYrN27f1Za2nFlZp6Wn189BQeHKigoWM2atVDz5i3VpEmzCvnvVZa8vDzt2/ebduzYpISE33X8eKrs9vNq1KixWrZspaCggt+7li1by8/P/5rec/PmDTp9OkOSVK9eA/Xo0cd4zm63a/v2jdq+fZOOHTuqEydSZbV6KySkjUJCWqlly9bq3PkWeXhcvjtnt9u1YcPXcjgcpf5DnpAQrzVrPi/1tTfd1EXNmjUvMX7gQEKJ9+rc+RYFBjaRJGVmntHatav122//1dGjh5Wefkr16tVXw4aBCgpqocaNm0qyGK+1Wr11xx39L/+bdBnfffelcnJyjGOLxaK+fe9z2kVJbm6u1q1bo/z8fElSq1ZhxSY6T55M05YtG/TLLz/p+PFUnT6drvr1A9SyZWu1bBmqtm07lDoxeq0OHTqg7ds3ae/e3UpLO67Tp9NVo0ZN1avXQI0aBermm7uoS5fuCghoVK6vMz39lDZtWmd8HWlpx1WvXn0FBbVU06ZBuummzurY0Vau/4ZU8Gf1l1/iFBe3RYcPH1Ra2nHl5eWpefOWat684PtEUFCwWrZsXSUau8nJifr111+ueM6aNZ/riSeiVaNGDSelAuBsJ04cU1zcFuP4lltuVYMGDY3jgweT9OOP32nfvr06deqEsrIy1aRJkEJCWqtFixB17Gi77mXgL1y4oN9//1Xbtv2o5OQknTp1Qunpp+TrW1cBAY3UuHEzdelyi26+uavq1PEp19eZkpKs77//VgkJvys19Q9lZp5RYGCTi9d5zdW9ex95e1/ftXFR6emn9NNPW7Rz53YdO3ZEJ0+ekNXqreDgguuwoKBgNW3aXC1btrri9ZErqOivxRl/1n799Rdj3+aUlOQSz69du0oNG5Z8j8td650+naHNmzcUGwsODlW7djdJKrjW2rLle23d+qNSU//QsWNHVbt2HTVseINatAhRUFALWSzFf29uu62fateuc8Wv48927dqho0f/KDb259+/ysbPn/z8WdHq1PE1OwIAVBtc83PNfzkVfU3ijGvGH35Yq7Nns0pdqTUjI/2y88hNmzbXzTd3KTHOPPIllf05wJWcPJmmn37aol274nTixDGdPHlC+fl5ql8/QA0aNFTbtjeqa9ceCg4OlcViufobXobdbteOHZu0bdtG4+uoUaPmxXndgs9N+vw/9u48zsby/+P4ezZmxjIzZoxlzGRPKCHZ17JUou2LElGhtOmnvW+qb2mlBaVop6JIZE0ooVSEiGzDjJ2xmxnMOL8/phkz7uvMnDPO7vV8POoxc51z3/d1m/vc53Nf1+e6rtZXFXv/udzRT+MJM2Z8ZSzv2bPfed+j4T0kWwB2zJnzrebOnZ73e3R0jJo1a6Pt27fq5ZefNk7JuXNnSl5jYaVKCXr66Vec6kg9ffq0Zs6cok8/fc8YzOQ2AC5f/rO++OIjxcXFa9Cgh9WqVQenv2TXrFmpt94arp07U4zH+f33ZZKkDz4YnRf4FEdKSrI++ugd/fLLT8bjnBtoXXfdzerff7BTUyV5yu+/L9OHH442/u13796p1av/KFA2aNDD6tatR5EB2zvvvK4DB/bl/f7ll3NVrlysfv11sV555RnjtZCcvFkLFuT83LRpK/3f/w1TdHSMcf979uzUm2++aPf4ycmb9dZbw42vDRjwkG655XZL+ZIlCzRx4gcFyoYMeVrXXHODpk+frHffHWHZ5sCBfdq0aYMSEy9Saup2y+u1a9dVQkKi3XqeKzV1m1577VlLecuW7T12/aSl7dcbb7yQ93u1ajX13ntfKisrS1OmTNDHH79r2Wb37p0FOuB79rxDffve41Rgn5y8WePGvaWVK5cbX8/tBJk/f6YkqUuX7urTZ6DTSRfp6emaMOF9ffPNF5bXcv+euZo2bWXppHDU6dOnNW/edH3wwWjj9b5zZ0qBe0hcXLweeugpXXlly2Idz1O+//47S1m1ajUL3EMyMtL1xx/L1Lx5W09WDYAHrVmzosD37M0399bAgUN07NhRjRr1shYv/sGyze7dO7Vixa95vz/xxItONyYtX75EY8eOMN6bDxzYl3cvyn3Q799/sG644Vank9n27NmlMWNezYsdzz3O2rWrJEnjx486r3vdkSOH9dVXn2rKlInG18+Nz+rXv1wPPfSUkpKqFfuY7uKuc/HEtfbtt5OM+8n1+ecf2n3NtN+tWzda4tAOHa7RJZdcqjVrVmr48Cd0+PAhy3YpKclaseJXJSQkWZ5nQkJC1KnT9Xbrca7s7Gy9/PLTluO8+OLbHk224PmT58/iysrKMi5NV716LS/UBgAuTMT8xPzncldM4omY8Y03XrC7JK4ku+3ITZu2MiZb0I58lrv7AUyOHDmsiRPH2+3kz71/LF78g8aPf1v161+ugQOH6OKL6zlxZjmJ1XPnTte4cW8ZzyO3vVqSJkwYp4MHDzi1//zc1U/jbmlpB4zfBxERkercuZsXagRX8e20P8CHfPPNl9qwYa0GDuzp0Nqnu3fv1P3399EPP8xyaP+HDh3Uww/fqffee6PQYCa/Awf2afjwJ/Xyy0/r5MmTDm1js9n04Yej9eijg4wNXefKyEgv9lqvc+dO14ABPYxBpT2zZk1Vnz7Xa9GinJkYfMHp06c1atQr+u9/H3Lq3+L999/U/ff30T//rHPqeHPnfqsZM77Ss88OdehaWL58iQYN6mlsYPO0d9553Rgg59ezZz+1aNHOUr5w4WynjrVkyUJLWatWHbzaUJqcvFmrV6/Qq68+Y0y0MJk8+VMNHTpAx44ddej9c+Z8q3vuudVuooXJ3LnT1bv3dZZAszApKckaMOA/xkQLk+XLlzi87/zS0g7okUcGavToV5269z3zzBANH/6k0tL2F+u47paZmanZs6cVKEtKqqYBAx6yvPf772d6qloAfMDUqZ9r9+6deuCBvoV2Wuf3yiv/1ahRrzj03qysLL355osaNuxhp5LgPv74XQ0e3Fv79+91eJuff16gO+7obmx0NXEmJswvJwbvYbfR1WTt2lUaMKCHJkwYp8zMzGId1x08eS7uvtbcadGieXr00UHGRItcHTt2VffuPSzl+RufHbF+/V+W40RERKphwyud2o+r8fzpvEB5/nSWKcE3OjpGVavW9EJtAAASMX9xBFLM78mYxN0xoztd6O3Ikvv7ATZsWKs77uhuN9HCZO3aVXrwwX5OfRZPnDiup59+UG+//ZJD57FzZ4rDzyD5ebqfxtXWrVtlLO/evQezWvg5ki0AB61e/Yceeqi/09uNGfNakZ2oBw+maejQAcXuLF+8+Ac9//wjeUsZ2GOz2TR27Eh99dVnxTqOMz777L1CZ1MoTEZGul555b/6/PMPin6zm508eVLDhj2sWbOKtw5pcvJmPfhgP/35528Ob/Ppp+/pnXded+o4hw8f0nvvjXS2ei41a9bUIgO3iIhItW59tbp06W55bebMqcrKynL4ePPnWx8KrrrqGoe3d5fHHrvH4QfpXBs2rNX06ZOLfN+0aV/azR53xGOP3etQwsW2bVv04IP9CmRau8Pu3Ts1ePBtxuVtHLF48Q+6665bdPBgmotrdv6WLVtkeWi4+uprdemljSxTai5b9mOhnUkAAk+/fjc4PRvQrFlTi7yHnzlzRsOHP+l0Z3OunTtTdP/9fZSWVvQIkx9//F4vvvhEsY7jjOXLl+ihh/oX+z45ceJ4PfPMkCLjZE/wxrm461pzp82bN+iVV/5b5Pu6dfuP2rWzjv5ct261cYkTe37+eYGl7JprbvD6qCeeP50TKM+fzlq//i+9/fZLlvKhQ5/1i2X3ACCQEfM7LpBifk/HJO6MGd2JduQc7uwHWLt2lR56qH+xkhokafz4tzVp0sdFvu/EieN65pkhTg0MLA5v9NO42vr1fxnLu3WzDiKAf2EZEeA8XHfdzUpISNSJE8e0detmY7ZqRka6Zsz4Sr173213P59+OtbuKJ+mTVurXr3LFBcXr+3bt2rDhnXGoHvFil/100/zC51uburUiYV26NauXVeXXHKpoqKi9c8/f2vNmhXF+jJesGC23SmEK1VKUN26DVSxYiUFBQVr27YtWrv2T2MwPWHCODVv3lY1atR2ug6u8u67r9sNFGrXrqtatS5RbGycMjLStXXrpgLT/+U3YsTzGj/+62KtoxwXF68OHbooJiZW+/fv1T//rCuwDEWutWtXafXqFWrQoHGB8nLl4tS9e0/ZbDZt27ZFa9asKPB6dHSM2rTpaDx27nrZjji3sbZWrTq6/PImioiI1G+/LdWGDWt14429FB4ersaNmyk6OqbA3/3w4UNavfoPNW7crMhjJSdvtnxmIiIi1bhxc4fr6yl16tRXkyYtFB4eoV27dmjlyl+ND9tTpkzUDTf0UunS5rWVd+5M1XvvvWF8LSEhSY0bN1OtWnWUkZGuTZs26Pfflxo/V2+//bLGjZtstwPh6NEjevzxe+1+9qOjY1S3bgPVqFFbR48e0Z9//lZgGjhHpaena9iwh+0+SDdq1FTVq9dSZGQpHTyYpuTkTcbrPiMjXWPHjtDTT7/sdB3caeZMa+DfuvXVKlGihFq16pC3xEuun3/+Qddf/x9PVQ+Aj2nRop0uuaS+srOzlZKyTb/88pPxPjxx4gdq0OAKu/tZtGhu3vSw56pXr4EuvbShkpKqad++Pdq4cb3xvYcPH9Lnn3+gBx+036i6bt1qvfzy03ZfT0hIUt26lykhIVGpqdu1YsUvxWo43bZti4YNe9j4Wu7MA1WrVldISKj27Nmlv/9eY4yp16xZodmzv1HXrrc4XQdX8ZVzOd9rrXnztoqOLifJvNZsmzZX572eX0REhMN1PDeuiIuLV9OmrVWhQkVt3vyPFi/+QXXq1Fft2nUlSe3addKPP35fYJuFC+fojjvuLfJYWVlZmjdvhqW8XbtODtfXk3j+NAuk589zZWVlKTMzU2FhYQoJCdHJkyeVlrZfe/fu0owZXxvv4z169PX55fbg3xITE9WoUc408fHxzi1VCVzIiPnNfCVOdgVfiUnON2a8+ebeOnr0iE6ezDTGyvY6hp1Zzo52ZLPz7QfIdfr0aY0Y8ZzxtejoGDVu3FyXXFJfoaFh2rp1k1at+t3Yvvvxx++qbdtOqlQpwbgvm82mV199xljHXI0aNc27NtauXVXoewvjC/005+uvv1ZayhISkjy6fCXcg2QLoBhuvPFW9e07SJGRpQqUr127Si+88JglSPr66wm66abexkbGTZs2GDORo6Nj9OKLo4xByuLFP2j48Cct5R98MEotW7ZXiRIljMcZP36U8XyaNm2tRx99zjJtVnr6CT377FBL53xhtm/falz/TJLuu+9RXXfdzQoJCSlQnpmZqU8/HWtcruDNN1/QW2997JWRZd9//53xbxMXF6+hQ4epUaOmltf27dujt94abvkyP3Bgnz777D3dc8//OVWHoUOHqUOHayznP2vWNxo1ytq5PHnyJ5Ygq2zZKA0e/IiknPXMzv17NmnSUvfd96hT9XKk3vnXyu7d+26tXr1CF11UXZIUGhqq6667yfIA8v333zkUJJumfrvqqmtVsmTJ86y560RHx2jYsNdVr16DAuWZmZkaP/5tzZw5pUB5Rka6Zs2aqp49+xn3N37828by7t17auDAIZZr5Nixo3rppacsQejOnSmaP3+mrrnmBuP+Ro9+xe7D8bBhr6lly/aW8qVLF+l//3vMuI097777ujGIb9DgCj300FPGdRfXrl2lESOesySrLF78g9q162Ssmzds27bF8uBQp059Va5cRZLUqlV7S7LFvHkzSLYALkD161+uxx9/QfHxFQuU79+/Vy+//LTlXrJmzQqtW7fa8t0i5SSxjRv3lvE4jz32vK666lpLeXLyZv33vw9ZZjKaNWuqbrihp3Ht4/T0E3r9dXOsl5CQpGHDXlPVqjUKlOcuI/D11xOM25mcPHlSzz031Phat2491K/fvZZpNm02m+bNm2EcSTZ69Ktq0qSlKlSo5HAdXMUXzsVV11qHDl3UoUMXSTkzc23c+HeB1/v3vy/v+84Vuna9RffeO7RAnHP77QMKjFrs3LmbJdli9uxp6t17QJHPEGvX/mnp5EhISHJ6fWJ34/nTvkB6/jRZtGiuFi2a6/D7H3jgca92MuHC0KtXL/Xq1cvb1QD8BjG/fb4QJ7uKL8QkrooZ+/QZKCnnOjg32SI6OoZ2ZDdxRT/A2W2mGgf71avXQM8+O0JRUdEFyrOzs/XJJ+8aZ6T77LP39fjj/7N7HHtLS/fufZduu+1uy/mkpm7TI48MdCo5yxf6aVzBNLNglSpJHq8HXI9lRAAnDRw4RPfc83+WoEXKCZ6HDrUGVRkZ6dq1K9VSbrPZjFM+VaqUoNGjP7ObDdqmzdUaNeoTS/mBA/s0e/Y3xm1MX8iS1LfvID333Ajj+mSRkaX0wgtvqUmTFsZtTSZOHG8pi4iI1McfT1O3bj0sQaUkhYeHa9Cgh/Xss9YpszZt2mBcg9bdMjMz9eGHoy3l9etfrg8+mGL8Apek+PiKGj58lG6+ubfltWnTvtT27VsdOn5ERKRee+09dep0vTGovu66m9Sz5x2WcnsZm570yivvFAiQczVo0FjR0TF5v1911XWW9/z44/c6evRIofu32WxasMC6Ll9uw78vSEqqptGjPzM+GIeHh+uBBx7PG42Z34YN5nXjVq/+w5iF3r//YA0e/IjxGilTpqxeeOEttWlzteW1Tz55VxkZGZby339fZlwCJSEhSR988LXdZIaWLdvrpZesnxd7tm7dZEk2kHJG4b366rvGRAsp5/M3duyXxn/XUaNeVnZ2tsN1cCdT1n/Hjmev98svt64Bv2nThmKvTw7AP7Vr10kvvTTG0ugqSeXLV9Czz5rXrt2y5R9j+TfffG5syqAysQAAIABJREFUrHjttbHGRldJqlatpsaMmaCEBOvD/UcfvWPcZtKkj42NNi1atNPo0Z9aGl0lKSgoSHff/WBeo50j5s//znic558fqfvue9S4nmlQUJC6dOmujz+eZlmyScoZleMN3j4XV19rntKnz0A98MDjljjnoouqq1q1mnm/N2hwheLiCo6sPnz4kEPT2C5ebF1CpHNnaxzrTTx/Fi5Qnj9dYeTI8SRaAICPIeYvnLfjZFfydkziypjRk2hHdn0/wJEjh/XJJ2Mt5c2bt9VLL42xJFpIUkhIiO6664G8AZv5LVw4x5gkcOTIYY0e/arxfJ5//g317XuP8XwSE6vqrbc+tjzD2ePtfhpXsbfsTYUKlT1aD7gHyRaAEwYNeth4c86vceNmxi+K/fv3Wsp+/XWx1q5dZSnv12+wMQjP7+KL66ljx66W8k8+GauTJ08WKPvrrz8to86knGzX3r3vVnCw/VtBeHi4rr7aGsyYJCdvNnbUPvXUSw6NcGvRop06dLCulfb332scOr4r/fDDTMvDS0REpJ544sUip0EOCgpS3773GKfXMv0dTF577T27mam5One2rlcnyavr7XXp0l0NG1o7kk0SEhKN0yIuXjy/0O02b/7H8iAWFxevunUvc7yibhQdHaM33/ywyM9wt27WWQx2795hfO/YsdZG8YSEJPXoYQ208wsNDVW/foMt5YcPH9KcOdMs5fZGH7z66rtKTKxa6LEaN25md0q5c3355UeWsnr1GuiOO+5VUFBQodtGRETowQetIysPHz6kvXt3O3R8d8rMzNScOd9aylu0OJuoEh4ebkyCcWbUIgD/1qJFOz3++AuFjqSJioo2xnqme11a2n5NmDDOUt616y2FTkEsSTEx5dSrl3VWpV9++UkbN64vUHbixHHNmPG15b3VqtXUf//7irExND/TWrsmp06dMp5Pz553qFmzNkVuX7lyFQ0aZJ2K+M8/3buGrIm3z8XV15qnVKqUoF69HFt7OiQkRNddd5Ol3JTYmd/p06e1cOEcS7m95fW8gefPwgXS86crDB06QE899YBX16EGAJxFzF84b8fJruTtmMTVMaOn0I6cw9X9AJMnf2Jcomjw4EcUHh5e6HGuu+5m43Vi+qzOmmVdQlmSHn30OTVr1rrQ41SqlODQ51zyfj+Nqxw/fsxYXqFC4c9h8A8kWwBOMGUAnyskJMQ4+vvAAWvgsmqVde3buLh4tWrVwaH6XH+9ddRKRka69u0rGJCb1lOW5HADpqOWLl1kKWvXrpNT68Wasp+3bfP8aO9Fi+ZZygYMeEjly1dwaPvw8HDdfvsAS7lp2QQTRwLxhIRE4zV57rSAnuRM9rok41IWpinB8vv5Z+vDyzXX3FBkJ72nlC9fQaVLlynyfY0aWae527Nnl6Xs4ME044wHvXr1K7ShOldCQqKaNm1lKT/3YTo5ebNxPe7eve9y+Lp3RGZmpvEB9KGHnnJ4asSqVWuoc+dulvLU1G3nXb/ztXTpQssDTZMmLVSuXGyBstatr7JsO2/eDLtZzgACS5UqSQ7dw03373379ljK1q//y7j9jTfe6lB92rTpaBwVdu6IukWL5hobbQYOHGIcqVVc69evsTSmREfHqG/fexzeR+fO3YyzHXg6KdXb5+Lqa81TTEukFcbUOb948Q86cuSw3W3+/PM3y/Vcv/7lDiePegLPn4ULpOdPV1mx4lc98cR9mjz5E9lsNm9XBwAuaMT8hfN2nOxK3o5JXB0zegrtyDlc3Q/w668/W8o6duxaZHK1lDNwz3RPOXeZv6ysLOOSI/XqNVCLFu2KPI4zvN1P4yonThw3lsfHe37ZI7geyRaAG+Sf4iqXKUg2dQzecsvtDjcsXnxxPeO6evmzNbOysvT778ss77nzzvuM9TwfpuM4E1RKOVmN5wb+mzZt8GgH5IkTx40jvhxZAy6/6tVrW8pcvUyAL2UkX3ZZY4en/8rVvHlb499769ZNxvefOXNGCxZYRyC2bes7IxAdZZqyLSMj3RJ4mWa7iIiIVNu2nRw+likxYefOlAK/mxItIiIidfPNtzt8HEf8/fdqS1lcXHzeOoyOqlnzYktZSopnp38zmTXLOpW2aWpC0/3k8OFDjEQEUEB0dDlLmSkx79x7upTzHevo2p/h4eHGEWjnfgf98Yd1mtLGjZvZnbazuEzfSc2atXGq8z04OFh16tS3lO/Ysf286uYsfzkXR681T2nSxLlniPj4isaOip9++t7uNqbkT9PIUn/A8+dZ/vj8WZj27bto+vSfNXv2r5o373fNmvWLPv98lkaP/lTDhr2mpk3NowY/+ugdjRxpXlsbAOBbiPnP8sU42RH+EpM4GjN6Au3IznOkHyArK8t4r+jevafDxzEtSXRum3Vy8mZjUtZddz3g0kQWf+qnKYq9ZDVfSvxB8Tn+zQXAYTExsZay48etmWum9feqV6/l1LGqVLnIkoW3d+/ZgNzeF58zHbWOOHnypDZsWGspnzBhnObNc27NW1N99+7drYSExGLXzxmbN1vXIJOkN954wan9mM5j06b1hncWX7lycZYye1mS7mZar7EouQ9606Z9WaD8hx9maeDAIZb3//PPOkvGbu3adYtc4sIXhYaGKiIi0nKdpKefKDAloynZokaN2oVOQ3kuU4bsuY3tppGObdt2LHJ6SGeZpkA8cGCfHnvsXqf2Y5rqbft2z2Yknys5ebPWrbMmk5gawUuVKq2mTVtr+fKC2eYLFsxxap1yAIHN1Bh27Jh1TdqdO63r7JqS0gpjGs1/bgfqqlW/W95z1VXW6W7P1+rVKyxlc+dON67nXPh+rN9tqanbdMkllxa7bs7yl3Nx9FrzhISEJIWFhTm9XefO3bR8+ZICZbNn56yRfa7MzEzjMiOm0X7+gOfPs/zx+bMwoaGhBaZ7Dg0NVVxcvOLi4lW7dl21bNleycmb9dZbwy3/HvPnz1SXLt1Vv/7lnq42Alxqaqr27s3p3Klatari4qztEgAcR8x/li/GyUXxp5jE0ZjRE2hHdp4j/QD2kmecGeQWE2NNAJNyrsXcZ4d166wJEHFx8apXr4HDx3GEP/XTFMVeG7svLIuN80eyBeAGZctGFfmejIwMy1RpkhQb61xGpymjcdeus52zpo5ae9udD3uNsbt373Q6SDY5cyb7vPfhqEOHDhrLTcG7s85dz/h8uXp02PkobuDfsWNXS5A8e/Y09es3WCVKlChQ/vPPCyzbd+58fbGO6wvKl69Q5JRlu3ZZH6YrVKjs1HFMwXhGRrqOHDmcN8OGaWpCR0dHOMOdny9vj0D8/nvrQ3S7dp3sBtNt2lxlSbZYtGiu7rvvUZUpU9YtdQTgX0qXduxesH27dWYfZ5eAMjW8bdu2Je/nI0cOGRsoKld2fWdkWtp+Y7krviuysz0XU0r+cy6OXmue4OxsV7muvLKVJZE1OXmzNm3aoFq16hR4r2kt7zZtrvbb71+eP8/yx+fP81WtWk3997+vaMCAHpb79Mcfv6ORI8d7qWYIVJMmTdLkyZMlSU8++aRuvvlmL9cI8G/E/AX5WpxcFH+KSRyJGT2FdmTnOdIPsGeP9ZqLjo6x/NsUJSEhyTJDxp49O/OSLVJTrbPHVKtW06ljOMKf+mmKYlrGSSLZIlCwjAjgBo6sx2f64pOcb4SKjbV2oubPdD561LpOcVJSNaemZHPE0aPuHfkWF+fcw8P5MP2buUpxG4/tceRa85SSJcOLfpNBjRq1LQ3gGRnp+uOPglPwZWdna8GC2ZbtW7W6qljH9QWOrHVpCl4rVnRuLTfTkiVSwVGIpsZ3dzxMHz5sDpJdoVIl55JQXCkzM1Nz5nxrKW/fvrPdbexNkb5kyUKX1QuAf3P0e94VDa+mxLyUlGTZbDZJ9mO9ihWto+POlysaJe1xZJ1aV/KXc/GlmDI8PKJY24WFhen662+xlC9caJ06+KefrEuIuGPEpqfw/Ol6nnz+dIXy5Sto8OBHLOVr167Sjh3WqaQBAL6DmN/1PBnz+1NM4ksxP+3IznPk72catFecz2+FCta25/yfWVPMX6XKRU4fpyj+1E9TlNxZrs9lWvYF/sd37q7ABcZeY5MzSwPk7Mc6xW7+DGRTwOdsIO6I9PQTLt9nrpw164rX6Foc7lyGo0YN56b3u1Bce+1NlrK5c2cU+H3dutWWhIDmzdv61Owe7mD6jJ86dcrJfZjvN5mZmZJyZoQwjVwwPYSfr2PHjrp8n7kSE61riHvK0qULLf+GERGRatjQ/pqmUVHRxjVPTdOaA4CznJ3tx96yDbmjwo4fP2Z83d4Uo8Xl7lFozs4OdT4C6Vz8RceOXS1lc+Z8W2DUUnp6uhYtmlvgPRERkWrUyLl1f/0Nz5+O8/Tzp6vUrWuetjl/gjUAILAQ85t5Mk4mJvE82pHtCwmxxvzZ2c7PBGy6V2RmZuT9fOSINQmiXDnrzDnnK9D6aUxLOJoSZOB/WEYE8BJ7I7aOHj1idxS6yaFDaZay/OvvlShhbTw7ePCAw/t3lL1s1MmTv/e7ICYyspSlLCmpmsaP/8oLtbkwtGlztd5++6UCZcuX/6y0tP2KjS0vyTz1mz+PQHSUKeP1zJkzTu3DXoJD7khGe5nRpsD5fJmW1Ljppts0aNDDLj+WJ82a9Y2lrHbtusZ7dH41atTWypUFpzJft261duxIccsyLgACU3R0jCXhy9kGFdP0nNHRMXkdtPYaZo8dO+rSpRfszfr07LOvq0WLdi47jicE0rn4i6SkaqpXr4HWrVudV5aRka7fflui1q1zRrGdO+pNkjp1ut7pqXX9Dc+fga9iRXPH0p49JFsAQCAg5vdNxCSeRzuyfaaYvzjLLu/bt8dSln+2i/Bw63XvjnbkQOunOfdZVcqZMSQ1dZsSE6t6qVZwBWa2ALzE3ohxZ6fYN33xVa5cxfhzruTkzU4dwxH2Gujsrdnry8qUsa5fl5KSXKzABI4pXbqMcSTiokXzJOUEheeO9o+IiLS7DEMgqVzZOtWbvXUt7TlwYJ+xPHdaxeDgYCUlWWeF2LfP9WvGRUVZHzRNU2H6k+TkzZZAWcpZP/COO7oX+t/XX08w7vPcEbcAUBjTdJ0HDxae7HWuAwf2WsqqV6+d93OlStaYUjLHoufLtKzBjh3WZbX8QSCdi7+45pobLGX548iffppveb2wZb8CBc+fgc/e7CXMbAEAgYGY3zcRk3ge7cj25U+CzlWcxFvTNvmXI0lIsA4Q27XL9dd8oPXT1Kp1ibH8yy8/9nBN4GokWwBeEhISYuzcLGoU9LlMX3yVKyfm/WwvSHZ1pmHZsubA0h+DZHtT47njwcIXODtLgrt06dLdUjZ79jTZbDatWbPCkr3frl1nYxZtoDF9hvfvtz4cF8aUbFGpUkKBBtmqVWtY3uOO9TNjY61Tym3Z8o/Lj+NJ33//ncv3OXfudLdPqwkgcJgbXp0bSW7uQD0bU0ZFRRtnW3JHJ55pfdiUlG0uP44nBNK5OKI4U9S6WsuWHSxly5cv0YED+3TixHEtWbKwwGuVKiWoTp36nqqe1/D8GfjS0lw/gwhgkpiYqEaNGqlRo0aKj7d2lgJwD2J+30RM4h20I5uZltDJyEgvsKxiUdLTTxiXm86fyJGQkGh53R3XfKD101x6aSNj+YIFs5Wa6n/3P5xFsgXgRaYg+a+//nR4+927d2rjxr8t5fmnD7W3Rt2ff/7m8HEcER4ertq161rKV6507XE8wV6G4erVf3i4Jp7hbAOru9Sr18CSfbtzZ4o2bFirxYt/sLy/Q4cunqqaV+XPGs61bt1qu+tomixdushSlpRUvcDvpiUrli9f4vJM4Tp1LrWUHT58yG8DyszMTM2Z863L93vgwD6tXev49wGAC5tpJLkzsV52drbxu/bc/V50UXXLe1asWG4pO1/1619uKfvzz9+caiDyFYF0Lo5wx9SxzoqMjFTXrrdYyhcunKPly3+2lHfs2FVBQUGeqJrX8fwZ2HbuTDGW2+sEAoqrV69eGjdunMaNG6fWrVt7uzrABYOY3zddaDHJ4cOHfGJWAdqRzewlJ6xfv8bhffz662I7+z47gM6UYJ2Skuzy5esCrZ8mJqac2rXrZHyN2S38G8kWgBeZOjenTv1cp06dcmj777772liev4ErMjLSuD7cJ5+86/BxHHXlldapuBYunON3X36lS5dRvXoNLOXvv/+mDh8+5IUauU5wsPW2v3ev65eKKI6goCBjw/isWd/oxx+/L1AWFxdvfCgLRPbWXl64cI5D2x88mGZMBjj3/mMKknfuTNGCBbMdOo6j6tUz/91GjXpFNpvNpcfyhCVLFhizvatVq6lateo4/J/J/Pmz3F19AAHCXmLeli0bHdr+99+XGmczOrfh1bSG6MyZU1yeMNeoUVNL2YED+/T115+59DieEEjn4ghfSeLt2PE6S9ns2dMsMaWUM8rtQsHzZ+Cy2Wz65psvjK9ddpl5BB0AwL8Q8/uuwI1JzAnJvpBgTTuyfabkn+nTv3J4+6++sn4Gq1WrWaBfwXQ/kqTPPnvf4eM4IhD7aUzLXko5s1usWPGr0/vbt2+PW2ZdhnNItgC8qFOn6y1lGRnplqltTY4dO6qpUz+3lLdpc7VlrTjTVLq7d+/UnDnTnKht0dq0udpY/uabLyozM9Ph/fhCh+vVV1sbaDMy0vXBB6Oc2o8vnEt+pvUQd+5MUVrafi/Uxqp9e2uW8fz5My2d2Z07X29MHAlEpUqV1lVXXWsp/+abLxxaAmbWrKnG8nOv8fr1Gxrf99FHY5SRkeFATR0TGRlp/DuvWbPCsp5iUXzh8/Xdd1MsZUlJ1fTee19qzJgJDv930023WfYzf/5Mpaef8MRpAPBzl1/exPgdb7pHnctms+nrrydYyqOjY9SwYcEG0MaNmxn38dFH7zhYU8fUrdvA2Fk7YcI4v5sJKZDO5Vym0ZXr1q32Qk2s6tSpb1kyY/funVq+fEmBsnr1GhinwA1UPH+a+UJMeb7mzPlWv/zyk6U8Li7e7ohAAIB/Ieb3XYEak0RGRhqXldmw4S8v1MaKdmSzrl1vtpQtW/ajQ7NOrFr1u5KTNxe5zwoVKikhwZrIvWDBbG3atMGJ2hYt0PppLrussTGBRJKeeuoBp2YeTEvbr8ceu0cjR/5Pv/221FVVRDFcOHcYwAclJlbVdddZv/xGjXq50JtqWtoBPfnkfcbX+vUbbCnr3fsu43vffXeEPv/8A50+fdrusbKysoxLEJhcdFF1Y6fw7t079eKLjxc5g0J2drbmzZuhG29sZxx15kkdO3Y1PsDMnz9TH3wwqsjO5xMnjuujj8aoS5crjVnj3pJ/Hcb87HXIe1psbJyaN29b5PvatjVPtxWo+vQZaCnbvXunRox4vtDpFWfNmqqJE8dbyrt06a7q1WsVKKtcuYpuueV2y3sPHz6khx++s8iREuvX/+XwtW7vnjRy5P80b96MIpNIdu3aoRdeeFwDB/b06tSFW7du0oYNay3lnTp1dXpfLVq0M5abGs0B4FwlSpTQ3Xc/aCmfM2eapk+fbHe7rKwsjRz5P61du8ry2sCBQyxr2rZt29E4G8+yZT9q+PAndejQwULr6UiHrpRzPv37m2PdZ54ZYqzvudasWal77rlVo0e/6tAx3SWQzuVcpimmp0370idGuknSddfdVOR7TM8ugYznz4J86fmzuPbu3a333ntDb7/9kvH1zp2vv2CWyQGAQEfMb+UrcXIgxySmWRKmTv3cJzquaUc2u+qqa42JEM89N1T79++1u9369X/puecesZRXqpSgLl0KzsYQGhqqgQOHGPfz6KODjEu55Hfo0EGtWPFLoe/JFWj9NMHBwXr44f/aff2RRwZq4sTxhc4KaLPZtHDhXN111y155/TSS08pLe2Ay+sLx5BsAXiZqSEqIyNdjzwyULNmTS0QwGZmZmr16j80dOjdxgzBm266zTgyKza2vPr2HWQ8/mefva8HHuirZct+VGrqtryOywMH9mnBgtm6997bivxyzK9Pn4HGjNfff1+mvn27acqUidq+fWteA1tWVpZ27EjR0qWL9NBD/fTGGy8oIyNdY8eOcOloemeFhYXpvvseNb729dcTdPfdt2jx4h+0Z8+uvI7hjIwMbdmyUbNmfaM777xJkyd/KklOZ1m6U1hYmGWUnyR9/vmH+vjjd3TixPG8srS0A5ox4yvNnFl0hrwrde5sHXGXX61adYyN+4GsUqUE3Xxzb0v5ggWz9eST92nLlo0Fkg727Nmljz4ao1GjXjHuz5S8IUm9evU3fn6Tkzdr8ODe+vTTsVq//q+8TpTTp09r3brVev/9NzVkyJ0On09iYlXjTA6S9MYbL+jxxwdr9eo/Ctz/jhw5rPXr/9Jnn72n/v1v1JIlC5WSkqzZs79x+LiuZm+KtlatrnJ6X5dccqnx337ePKaBA+AYe42i7747Qu+887p27dqR1yCWlZWlrVs36X//e9Q4q1C1ajWNSysEBwdr4MCHjcdfvPgH9e9/o2bPnqYtWzYqPT1nNNGJE8e1fPkSDRv2fxo7dqTD53P11dcZG/Z2796poUMHaPToV7Vx4995MwDZbDbt379Xq1b9ruHDn9Sjjw5ScvJmzZw5xeUja5wVSOeSX9WqNYzlDz98l9av/0vZ2dmSchqPV69eobFjR3p0NjVHGlVbtbLOwBDoeP70zedPe7Zt26zZs6dp6dJFWrHiVy1dukgzZnyljz4ao+eff1R9+3bTtGlfGretVauO/vOfOzxcYwCAOxHz+26cHKgxSY0atS1l69at1rPP/l+BJJLMzEwtXvyD3n//TU9Wj3Zkg5xEiIcs5cnJm3X//X20YsWveZ9dKafNdcGC2Roy5E7jUskDBw5RaGiopbxZs9a67LLGlvKMjHQNH/6khg9/UqtW/a59+/bozJkzstls2rZti6ZMmaj+/W90OPEhEPtpEhOr6s47zclmUs4MPz16dNT777+pX375SatXr9C6dau1fPkSffzxO+rf/0a9+uozBf5eGRnpevPNFxyaCRuuZ/2EAPCo2Njyuv32uzVx4geW10aNekWjRr2i6OgYxcTEGqdwyhUREalevfrbff2mm3prxoyvjGtZJSdv1vPPm7+wnFWpUoKee26EHn/cOsJJksaPf1vjx79d5H4OHz6kKVMm2O0U9oQWLdqpb99BxrXGDhzYp+HDn3RoP0uWLNSqVb/r8subuLqKxdKnz0Bj3SdN+kSTJn0iKed6yv2yrl27rnENPHdp3Lh5geOfyzT98YWgZ89+mj17muXfZd261Ro8OCcRo1atOtq/f2+ha9bdfvvdxmxgSSpTpqz69x+sd98dYXz9iy8+0hdffFTMMyjorrse0PbtW41r0a1Zs0KPPbbCof189NE7atu2k2X6anfLyMjQ3LnTLeX16jVQpUrmdQsLExoaqo4du2rGjIJrKK5e/Yf27NmlihUr29kSAHIEBwfrrrse0BNPWB/YZ8z4SjNmfKWIiEglJlbVxo1/F7qvQYMeVkhIiPG1yy5rpBYt2mnZsh8tr2VkpNsdXe2s0NBQPffcCA0e3Nv4vTZz5hSHE0Lfe2+kRowY57XR3YF0LvldeWUrJSVVU0pKcoHynTtT7CZhNm7cTLGx5T1RPcXElFPz5m3tzhLVokU7j8cPvoDnTzNfeP402bRpQ7Huq9HRMXr++TcUERHhhlrhQpeamqq9e3NGxlatWlVxcXFerhFw4SDmt8/bcXKgxiTduvXQN998YSlfvnyJZYm+XLffPkClSpV2d9Uk0Y5sT9OmrVWvXgPLMo+HDx/SU089IElKSEjSmTPZhSY91KvXoNDZQwYOHKL77+9jfG3x4h+cSqIuTCD20/TocYfS0g7YnZkoIyNd33zzhfHzZ8/atauUnn5CpUuXcVU14SBmtgB8QK9ed9pd203K+RIsqqHrhRfeKrSxMCIiQsOGvW7MsHW1yy9vouefH3nex1qwYLaLalR8t912l91RWc5YvHiBC2rjGm3aXG13/cVc+QPUjRv/zhuZ6AklSpRQu3b2RyKa1oC+EERFRevFFwt/KNu0aUOhiRZt2lytnj3tN4pL0nXX3awOHa4pVh2dERoaqqeeeqnQe58jMjLStXLlchfVynFLly40Psh16GBdL9JRLVualxL58cd5xd4ngAtLw4ZX6p57/s/u6xkZ6UU2ut5zz/+pYcMrC33P/fc/bpwpy9ViY8vr9dffV7VqNc9rP2vXrip0ulRPCKRzyRUaGqohQ552aptzEzPcrU0b+7NNdexoXfv3QsHzp5kvPH+6QuPGzTRixDiPJTbhwjNp0iTdc889uueee/TTTyx7CHgaMb+ZL8TJgRiTVKqUUOgIfJMdO7a7qTZWtCObBQUF6emnXy70M7xzZ0qhiRZJSdX09NMvF5rAVKtWHd1//2PnVVdHBVo/TVBQkO69d6i6d+/pkv3VqlVH48ZNJtHCS0i2AHxAWFiYHn/8BfXs6fwUn0lJ1TRy5HhdemnDIt9br14DjRw53rhmV2GKEyA2a9ZGn346Xe3bF6/j8aqrrtWrr44t1rauFBQUpN6979bYsV8Yp+lzRO/ed9ldw8xbHn74v8Zp+uwpam1BV6tZ82JjedOmrRQbe+GOmqlf/3KNGTOhWA+7vXr10xNPvKgSJUoU+r7Q0FA98siz6tGjr1P7L06dSpcuo6efflnDhr2m6OgYp7ePi4vX0KHD1L69ddpLd/vuO/PIihYt2hd7n/XrNzTeb00zaACAPTfeeKueeOJFp+O3iIhIPfnkcN14461Fvjc2Nk5vvPGBmjRp4dQxitOAmpRUTWPGTFC/fvc6va10Nv6Nj69YrO1dKZDOJVe9eg3sTulq4ulki2rVahnLIyIi1bhxc4/WxZfw/GnlK8+f56NOnfp67bWxeuml0UpMrOrt6gCDcgehAAAgAElEQVQA3IiYvyBfipMDMSa56abe6tixq8PvT03d5sbaWNGObBYbW14jRoxT06atnd62efO2GjlyvEPJu9df/x+n70fFifcDsZ8mKChIgwc/ouefH2l3JmpH3HbbnRoxwjfugRcqlhEB7AgPt063WbJkSYe2LVky3OltQ0NDdeed96tjx66aMmWifvppvt3pr6ScpR2uvfZGdezY1bhmlj01atTW++9P0syZUzR79rRCGzwbN26mQYMe1p49uzRsWMG1+iIji/5CjIqK1hNPvKB27Tpq+vSvtH79X4WeU61addSoUVN17tzduPavN1WvXktvvfWxvv12kn76aX6RGeINGlyhpk1bqVOn61WmTNlC32u6XoKDzdP4nSs83PlrTZLKl6+gN974QBMmvJ+3Zpk9CQlJyswsuFZgiRLWY5jOo7guusi8BrgnZlxwRliYNXHB9G9jj+nfLDQ0rNBtatWqo7Fjv9D8+TM1e/a0Qq/FiIhItW3bUT163OHUZyokJER33fWAOnS4Rl988WGhU75FR8eoZ89+6tathx5++C5LfRy5P7Vs2V6XXtpIEyeO159//lbofSkiIlJNmrRQixbt1Lr1VU7d/1zl2LGj2rBhraW8ceNmKlcuttj7DQ0NVbt2nTVnzrQC5bt379SZM2cUHEyOLOCPTPd6R78rTPc403fPudq376wrrmiuadO+0MKFcwsdrVKpUoI6dOiim27q7dQIiDJlyurFF9/Wr78u1tSpX2jNGvvLPyUlVdPddz+oOnXqq0ePjpb9FCU0NFS33nqnmjdvq88//0B//71GBw7ss/v+uLh4NW3aWu3addJllzVy+Jw8wZ3n4o1rTcqZWviSSy7Tyy8/rZ07U+y+Lzo6xrJPd8eU9jr5r7rqWoef7TyF50+eP515joiLi9dFF1VXQkKSKlVKUPXqtdSgwRU+scQQAFyIiPmJ+fNzZ0zi6ZhRyknUfeSRZ9WoUVONGvVyoecSFxevM2fOFCijHfksT/cDREVF63//e0MrVy7Xt99Osrv0S66mTVvppptuc3qZjfbtO6thwyv11VefasGC2YXOvNy16y2644579PXXn+mrrz4r8Jrp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AAAAQKO37bbbZvz48dltt90qx6677rqceuqpWbZsWYHJAOreAQcckJKSkiTJ/vvvn8svvzytW7cuOBUAADQuyhYAAADAJmGLLbbI2LFj87Wvfa1y7IEHHsgJJ5yQ+fPnF5gMoG7ttdde+Z//+Z8cccQRueCCC9KsWbOiIwEAQKPjUzYAdWrhwoV55ZVXkiRt27ZNr169Ck4EAAD/p1mzZjn//PPTs2fPXHnllUmSl19+OUcffXRGjx6dHj16FJwQoG4cfvjhRUcAgE81bdq0lJWVJUlKS0sLTgNQe1a2AKBOvfjiixkyZEiGDBmSUaNGFR0HAACqNWjQoJx33nlp2rRpkmTOnDk57rjj8uyzzxacDKB2Fi9eXHQEAFgnI0eOrJxLnj17dtFxAGpN2QIAAADYJB144IEZO3ZsWrdunST58MMPM3jw4Nxzzz0FJwOomSeffDLf+MY38uSTTxYdBQAANjnKFgAAAMAma/fdd8/48ePTpUuXJEl5eXlOP/30jBkzpuBkAJ/uvvvuy7Bhw7JkyZKMGDEi06dPLzoSAABsUpQtAAAAgE1ajx49cv3112fXXXetHLvqqqvys5/9LCtWrCgwGUD1xo8fn9NOOy0rV65MkrRt2zYtW7YsOBUAAGxalC0AAACATd5WW22Vq666Kvvuu2/l2L333pvBgwdn4cKFBSYDWNOVV16ZSy+9tPJ2r169cu2116Zr164FpgIAgE2PsgUAdapNmzbp06dP+vTpk169ehUdBwAAaqx58+b51a9+lQEDBlSOPffcczn22GMza9asApMB/J999tknLVq0SJL06dMnV199dTp06FBwKgCovd69e1fOJa/6bxtAQ1JSUVFRUXSIdTF8+PBMnjw5STJq1Kj07du34EQAAABAYzFp0qScddZZlUv0b7nllrnsssvSu3fvgpMBJJMnT85tt92W888/P5tttlnRcQAAYJNkZQsAAACATzj44IMzevTobL755kmSRYsW5Qc/+EHuv//+gpMBJP3798+vf/1rRQsAACiQsgUAAABANUpLS3PdddelY8eOSZIVK1bkv//7v3PNNdcUnAzYVDzzzDNFRwAAANZC2QIAAABgLbbffvtMmDAhO+20U+XY6NGjc8YZZ6S8vLzAZEBjd/vtt2fw4ME588wzi44CAABUQ9kCAAAA4FO0b98+V199dfr37185duedd2bo0KFZvHhxgcmAxurKK6/MOeeck4qKikyaNCk33XRT0ZEAAIBPaFZ0AAAal1mzZmXSpElJkh49euSggw4qOBEAAKy/Fi1a5OKLL85FF12U3//+90mSqVOn5rjjjsvo0aOz7bbbFpwQaAxWrlyZc889N3fccUfl2I477pgDDjigwFQAsGHceOONWbBgQZLk6KOPzhZbbFFwIoDasbIFAHVq1qxZGTduXMaNG5e777676DgAAFBnSkpKMmLEiJx++ukpKSlJksyYMSNHH310XnrppYLTAY3B+PHj1yha9O3bN9dcc03atGlTYCoA2DAmTpxYOZe8aNGiouMA1JqyBQAAAEAtHHbYYbn00kvTokWLJMmCBQtywgkn5JFHHik4GdDQHXXUUfnCF76QJNl///3z29/+Ni1btiw4FQAAUB1lCwAAAIBa+vKXv5xrr702W2+9dZLko48+yimnnJIJEyYUnAxoyFq2bJlRo0Zl6NCh+eUvf5mmTZsWHQkAAFgLZQsAAACAdbDDDjtkwoQJ2W677ZIkFRUVueSSS3Luuedm5cqVBacDGqp27drl+9//ftExAACAz6BsAQAAALCOOnXqlOuuuy59+/atHLvtttty0kknZcmSJQUmA+q7F154IUuXLi06BgAAsI6aFR0AgMala9euGThwYJKkR48eBacBAIANr1WrVrn00ktz/vnn57bbbkuSPPHEEzn++ONz+eWXp1OnTgUnBOqbRx99NKeeemp23333XHbZZbYLAWCTdOSRR2bBggVJki233LLgNAC1V1JRUVFRdIh1MXz48EyePDlJMmrUqDW+QQIAAABQhAkTJuS3v/1tVk23bL311rn88suzww47FJwMqC/uuOOOnH322ZW3jz/++AwbNqzARAAAwLqwjQgAAABAHTnmmGNy0UUXZbPNNkuSzJs3LwMGDMjjjz9ecDKgPrj66qvXKFp06dIlRxxxRIGJAACAdaVsAQAAAFCH9t5771x99dVp06ZNkmTZsmU56aSTcuuttxacDCjSvffemyuuuKLy9k477ZRrr702nTt3LjAVAACwrpQtAAAAAOrYzjvvnBtuuCHdu3dPklRUVOS8887Lb37zmzTQHV2B9fTVr341/fr1S5J86UtfylVXXZV27doVnAoAAFhXyhYAAAAAG8C2226b8ePHZ7fddqsc+/3vf58f//jHKSsrKzAZUIQmTZrkwgsvzA9+8IOMHj06LVu2LDoSAACwHpQtAKhTs2bNytixYzN27NjcfffdRccBAIBCbbHFFhk7dmy+8Y1vVI5Nnjw5AwYMyPvvv19gMqAILVu2zJAhQ4qOAQD1wo033lg5l7x48eKi4wDUmrIFAHVq1qxZGTduXMaNG6dsAQAASZo1a5azzz57jQus06dPzzHHHJPXXnutwGTAhvLGG28oVAHAZ5g4cWLlXPKiRYuKjgNQa8oWAAAAABvBD37wg1x44YVp1qxZkmTu3Lk5/vjjM2XKlIKTAXXpxRdfzPe///0MHTo0S5cuLToOAACwgShbAAAAAGwk++23X6666qpsueWWSZKlS5dm6NChmTRpUsHJgLrwxBNPZNCgQVm4cGH++c9/5uyzzy46EgAAsIEoWwAAAABsRL17987111+fLl26JElWrlyZM888M5dddlnByYD1cdddd+Wkk05KWVlZkqRjx44ZOHBgwakAAIANRdkCAAAAYCPr2rVrrr/++uy6666VY9ddd11OPfXULFu2rMBkwLqYPn16Ro4cmZUrVyZJdtlll0yYMCHbb799wckAAIANRdkCgDrVtWvXDBw4MAMHDsxBBx1UdBwAAKi3ttpqq1x11VXZd999K8ceeOCBnHDCCZk/f/46vWZpaWlKS0vrKiJQQ7169cqJJ56YJNl3330zbty4tG/fvuBUAFC/HXnkkZVzyau22QNoSEoqKioqig6xLoYPH57JkycnSUaNGpW+ffsWnAgAAABg3YwePTrXXHNN5e1tttkmo0ePTo8ePWr8GquXLHbaaadMmDChTjMCn+2JJ54wTwkAAJsIK1sAAAAAFGzo0KE566yz0rRp0yTJnDlzctxxx+XZZ5+t0fNLS0tTUVGRVd+pefnll/PSSy9tsLxA9RQtAABg06FsAQAAAFAPHHzwwRk7dmxat26dJPnwww8zePDg3HPPPZ/53IqKipSUlFT+nCQ333zzhgsLm7C//e1vefDBB4uOAQAAFEzZAgAAAKCe2H333TN+/Phss802SZLy8vKcfvrpGTNmzFqfs/r2IasKF0ny5z//OR988MGGCwuboIceeihDhw7N6aefnpdffrnoOAAAQIGULQAAAADqkR49euSGG27ITjvtVDl21VVX5Wc/+1lWrFixxmNXFS1WL1mssmLFikycOHHDhoVNyC233JIRI0Zk+fLlWbZsWc4444yiIwEAAAVStgCgTs2aNStjx47N2LFjc/fddxcdBwAAGqS2bdvm6quvTv/+/SvH7r333gwePDgLFy5M8nHRYtWWIatbvXjxxz/+sUpBA6i9MWPG5Pzzz6+83aVLl1x22WUFJgKAhu/GG2+snEtevHhx0XEAak3ZAoA6NWvWrIwbNy7jxo1TtgAAgPXQokWLXHzxxTnmmGMqx5577rkce+yxlUWL6la0WN2CBQty//33b+io0Ki98847mTBhQuXtXXfddY3tfgCAdTNx4sTKueRFixYVHQeg1pQtAAAAAOqpkpKSDB8+PKeffnplseKtt96qdkWLtbn55ps3VDzYJGy77bb5zW9+kyZNmuRLX/pSrrzyyrRp06boWAAAQMGaFR0AAAAAgE932GGHZdttt82wYcOS5DNXtEhSufLF1KlTM2PGjHTv3n1Dx4RGa88998yYMWPSp0+foqMAAAD1hJUtAAAAABqAYcOGpaKiolarWqx67B//+McNFQs2GYoWAADA6pQtAAAAABqAVcWJmqxqsepxqx47adKkfPTRRxssGzQW//znP3PbbbcVHQMAAGgAbCMCQJ3aZZddMmbMmCRJ27ZtC04DAACNQ2lpaZKaFy1WV1FRkYULF+auu+7KN7/5zbqOBo3G888/n2HDhmXJkiXp2LFj9tprr6IjAUCjds4556SsrCxJ0rlz54LTANSesgUAdWqrrbaqnAgGAADW3/oULVZ/3vjx43PooYeu8+tAY/bQQw9lxIgRlbfPPPPM3HfffQUmAoDGr3fv3kVHAFgvthEBAAAAqKf22GOPyu1D1tWq57/55pt59NFH6yIWNCq33357fvKTn1Tebtu2bcaOHVtgIgAAoCFQtgAAAACoh+pyxbhVhYubbrqpzl4TGoMlS5Zk1KhRlX9HunXrlvHjx+fzn/98wckAAID6TtkCAAAAoJ4pLS2tvPi7vtt+rP78yZMnZ86cOev1etCYtGrVKldccUVatmyZHXfcMddee20+97nPFR0LAABoAJQtAAAAAOqRT65oUVFRsd5biazO6hawpl69euWKK67I1VdfnTZt2hQdBwAAaCCULQCoUy+99FIGDRqUQYMGZdSoUUXHAQCABmfKlClJPl6RokmTJikpKUlJSckapYvali9WPT9JbrnllqxYsaJuQ0MD98UvfjGbb7550TEAYJMycuTIyrnkefPmFR0HoNaULQCoUwsWLMgzzzyTZ555JtOnTy86DgAANEhTpkzJ9773vXz+85+vHFtVulj186ryRW1XvliwYEHuv//+Os8M9d17772XCRMmFB0DAPhf06ZNq5xLXrZsWdFxAGqtWdEBAAAAAKjqlFNOSZIsWbIkf//73/P3v/8906ZNy1NPPZWPPvqosnixyuqFi0/et2qsoqIiJSUlueWWW/K1r31tw74BqEfeeOONDB06NO+++25atWqVww8/vOhIAABAA6dsAQAAQI0smr88zz/2Qd5+c0k+mLMsy5dXpGnTpLy86GSwKWiTpH92aN0/vb4yJCtWrEh5eXnKy8uzcuXKdXrFy/7nxTRpYtFTGr/y8vIsXbo0+/Q6O+mVvPJgMmrKS9WWkoBilZQkFRVJ881KsvU2LdK5R6vs3q9dWrdpXnQ0AIAqlC0AAAD4VM8//kGm3D83C+atyMffm69ISVyggmI1+fj/mjZPmq7bK6z4KEnWragBDUtJNmvWao2R5csqktR8+x1g4/qoLFm88MPMePXDPPWXuWnbYbOU7t8xX9yzbdHRAAAqKVsAAABQrece+yAP3/FOVixfbWuC1f4/AABsKJXl3pJk/vvLc98f3s79t8zOAf/ZObuUKl0AAMVTtgCgTu2yyy4ZM2ZMkqRtW//wBYCGaOni8tx21Zt5Z0ZZlV5F02bJFm02S6cerdKufYtiAgIA0KjNn7ssc2YtyaIPlmdl+f8Vf1euqMjd/9/bef6xD3LUSdsVmBCoC+ecc07KysqSJJ07dy44DUDtKVsAUKe22mqrlJaWFh0DAFhHH7z7UW645LUsL1u5RtGi4+daZo/9O6VTt9bFhQMAYJMz+40PM/WvczJvzrIkSUlJ8vabS3L56S/l2BG9stXWzQtOCKyr3r17Fx0BYL0oWwAAAJAkmTdnWSZc9FrKl6/8eBY7SautmuUbx/VMq61MYgMAsPF17tk6h5ywfT5csDx3XvdGli5ekZKUZNnSilx34as57tQd0qa9z6oAwMbXpOgAAAAA1A8TL38j5SsqKosWu+7ZLkecuIOiBQAAhWvdpnn+86QdsuMeH29bW1KSrFieTBz1WsHJAIBNlbIFAAAA+fP4WSlbXF55e/d9OmaP/bctMBEAAFTV98DO2bVv+8rbixeW5y8T3y4wEQCwqVK2AAAA2MTNmbE0rz63MPl4QYt023GL/Ev/DsWGAgCAtdhjv07p3LNVko8/wv79qQ8yb85HxYYCADY5yhYA1KmXXnopgwYNyqBBgzJq1Kii4wAANXDnDbNSUVGRVFRks5ZNsu8R3YqOBAAAn+qA7/VI0+Yft4VLkvz5ujeLDQTU2siRIyvnkufNm1d0HIBaa1Z0AAAalwULFuSZZ55JkrRq1argNADAZ3nv7WWZ/97ylJR8PFG950G2DgEAoGH4t/065Yl75iQpyXtzlmfB+8vTpn3zomMBNTRt2rTMnDkzSbJs2bKC0wDUnpUtAAAANmFTH3ovyapVLUqy3S5tio4EAAA1suMeW6fZZiVJRZKKikx58P2iIwEAmxBlCwAAgE3Ym/9YnIqUJCUl6fEFRQsAABqWbr22zMcfZ0vy2osLi44DAGxClC0AAAA2YR8uKk/J//68S9+tC80CAAC19YU92lb+vHjB8gKTAACbGmULAACATdQ7by6t/LmkJGnTvkWBaQAAoPY6dWv9fzcqSrLwfYULAGDjaFZ0AAAal1122SVjxoxJkrRt2/YzHg0AFOm9d8oqf27aXBcfAICGqWmzkpSvqEiSzHm7LFu1b15wIqAmzjnnnJSVffzv0s6dOxecBqD2lC0AqFNbbbVVSktLi44BANRAxcqkoqIiJSUladqs5LOfAAAA9VCTpiVZsWJlSpJUlJcXHQeood69excdAWC9KFsAAABsosrKylNS8r8li4piswAAwPooycefa5cu8cEWANg4rBMLAAAAAAAAAFALyhYAAAAAAAAAALWgbAEAAAAAAAAAUAvKFgDUqSeeeCKlpaUpLS3N8OHDi44DAAAAAEA9dNhhh1XOJc+ePbvoOAC1pmwBAAAAAAAAAFALyhYAAAAAAAAAALWgbAEAAAAAAAAAUAvKFgAAAAAAAAAAtaBsAQAAAAAAAABQC82KDgBA49K3b99MmTKl6BgAAAAAANRjt956a9ERANaLlS0AAAAAAAAAAGpB2QIAAAAAAAAAoBaULQAAAAAAAAAAakHZAgAAAAAAAACgFpQtAAAAAAAAAABqQdkCgDr1xBNPpLS0NKWlpRk+fHjRcQAAAAAAqIcOO+ywyrnk2bNnFx0HoNaULQAAAAAAAAAAakHZAgAAAAAAAACgFpQtAAAAAAAAAABqQdkCAAAAAAAAAKAWlC0AAAAAAAAAAGqhWdEBAGhc+vbtmylTphQdAwAAAACAeuzWW28tOgLAerGyBQAAAAAAAABALShbAAAAAAAAAADUgrIFAAAAAAAAAEAtKFsAAAAAAAAAANRCs6IDANC4LFy4MK+88kqSpG3btunVq1fBiQAAAAAAqG+mTZuWsrKyJElpaWnBaQBqT9kCgDr14osv5sQTT0yS9O/fP5dccknBiQAAAAAAqG9GjhyZmTNnJkn+9Kc/pXPnzgUnAqgd24gAAAAAAAAAANSCsgUAAAAAAAAAQC0oWwAAAAAAAAAA1IKyBQAAAAAAAABALTQrOgAAjUubNm3Sp0+fJEmvXr0KTgMAAAAAQH3Uu3fvdOzYMUnSokWLgtMA1J6yBQB1auedd86VV15ZdAwAAAAAAOqxc845p+gIAOvFNiIAAAAAAAAAALWgbAEAAAAAAAAAUAvKFgAAAAAAAAAAtaBsAQAAAAAAAABQC82KDgBA47Jw4cK88sorSZK2bdumV69eBScCAAAAAKC+mTZtWsrKypIkpaWlBacBqD1lCwDq1IsvvpgTTzwxSdK/f/9ccsklBScCAAAAAKC+GTlyZGbOnJkk+dOf/pTOnTsXnAigdmwjAgAAAAAAAABQC8oWAAAAAAAAAAC1oGwBAAAAAAAAAFALyhYAAAAAAAAAALXQrOgAADQubdq0SZ8+fZIkvXr1KjgNAMCmo6ysLNdcMyrl5eWVY507d8kRRxxTYCo2JucAUBOPPfZgpk59co2xb33ryHTr1rOgRGxI5eXlWb58eZo1a5ZmzVwOAOqX3r17p2PHjkmSFi1aFJwGoPZ8ugKgTu2888658sori44BALDJWbx4YW6/feIaY927b+dC+ybEOQDUxNSpT+bPf75pjbE99tiz0Zctnnrq0Tz55OQsXbokS5cuybJlZSkrW5qPPlqWZcuWpby8PCtXlqd5883SpUv3dOnSLZ/7XNd07759evfePSUlJUW/hXVyzTWjctNNE3LIIUdk2LCfFh0HYA3nnHNO0REA1ouyBQAAAAAAjdo//jGtSslkbWbMeH2N2zvssFOOOWZg9txz7wZVuigrK8ukSbckSf761zszcODJadmyZcGpAAAajyZFBwAAAAAA1q6srCxvvPHPomPAJuvVV1/OmWeOyPDh38+SJR8WHafGHnvswSxduiRJsnTpkjz11OSCEwEANC7KFgAAAABQD73yyou5/PJf5aijDsxPf/rDouPAJu/ll6fl4ovPTUVFRdFRamTSpJvXuH3ffZMKSgIA0DjZRgQAAAAA6pGysrIMG3bcGlsZtGjRosBE0Dhtvnmr9O+/X1q0aJmWLVumSZMmef/99zJnztuZNu3Zap/z8MP35Qtf2DVHHHHMRk5bO2+++VqV9/Dkk5Pz/vtz0759x4JSAQA0LsoWANSphQsX5pVXXkmStG3bNr169So4EQAAQMOycmX5GkULYMPo33+//OQnZ1Z73/vvz83NN9+Qm2++ocp9EyaMq/dli3vuuaPa8QcfvDff/vbRGzkNQPWmTZuWsrKyQNj8KwAAIABJREFUJElpaWnBaQBqT9kCgDr14osv5sQTT0yS9O/fP5dccknBiQAAAABqp337jhk0aHiSVClcLF26JO+++046ddq2iGifqaysLHfeeWu199199+3KFkC9MXLkyMycOTNJ8qc//SmdO3cuOBFA7TQpOgAAAAAAANRHAwYMrXb89denb+QkNffYYw9m6dIl1d43Y8brefXVlzdyIgCAxknZAgAAAAAAqrHZZptlhx12qjI+Y8ZrBaSpmT//+aZPvf/+++/aSEkAABo324gAAADQYM2dOydTpjxeeftLX9or7dt3rLz95puv5ZFH/ppXXnkp8+bNzeLFi/K5z3XL9tvvkB49ts9uu5Wu8xLgK1asyD/+8UKefPKRvPHGa5k3b24++GBettxyq3To0CmdO3dNnz5fyr/8yx5p3XqL9XqfM2e+kYce+kumT/9H3nnnrSxatDDbbvu5dOvWM127ds+ee+6dzTdvtV7HSJIPPpiXqVMfzzPPPJU5c97O++/PTcuWm6dnz17p1q1HunXrmS5dume77XqlSZP68f0N54BzYG3q+r28/PK0ym+yl5SU5IADDknTpk2TJOXl5Xnhhefy6KMPZObMNzJ37pyUl5enR4/t8/nP75ju3bdLaWm/tGr16X9GDz98Xz78cHG130ifP/+D3HXXbdU+r0uX7vmXf+lTZXzBgvl57LEH1xjr2fPz2XnnLyZJli9fnscffyhPPPFI3nnnrcyZMzutWrVOx47bpEeP7dOtW4+UlKz5v82///sBadWq9ae+j0969tmnM3v2W2uMffLv6ob22GMPZsGC+UmSdu3ap2/fvSvvKysry1NPTc5TTz2aOXNmZ+7cd9Ky5ebZfvsds/32vbLddjvkX//1S+t8zr///nuZOvXxPPvslMydOyfvvz83K1eWZ+utO6R9+475whd2zR579E3Pnp9PSUnJOr/HsrKyPP30o3nyycmV76N5883StWuPdOvWI9ttt0P23nv/dX79VcrLy/PKKy/m6acfzfTp/8i7776TsrKl6dSpc7bb7uO/X127fny8Nm3arvfx6qvWrbcsOkK13njjn3nhhec+9TF33XVbvv/9E9O8efONlAoAoHFStgAAAKDBev75qbnkkvMqb3/720dn0KDhWbRoYS699Bd5+OH7qjxn9uy3MnXqE5W3Tzvt3Oy774G1Ou6TT07OFVf8usrFwyR57713Ky/I3nHHH5Ik//VfQ/Otb303LVu2rNVx3nnn7Ywa9cs8/fRj1R5n2rRnkyTjxl2aL3/532v12qtbsGB+/vCH63LTTROqvf+TS6X37r17Tj75Z+nefbt1PmZdcQ44Bz5pQ72Xu+66LXfffXvl7bZt26Vv333y5puv5Re/OL3aLQXeemtGZdmhc+cuOf30C6r9hvwqF110zlqX/k+yxrm+uj337F9t2eK1116p8pz99vt6dt75i3n++Wdy3nmnZf78D6o8b8aM1zN16hPp0qV73nprxhr3NW3aNF/72n+sNeMnlZeX5xe/OL3Kcc4997cbtWxx+eW/ynvvvVt5+/e/vztbb90+TzzxcC64YGS1/7u//vr0/PWvH/+85579c8opZ6Rt23Y1PuaCBfMzYcK4yt8Dn7Tq98fDD9+XceN+m969d8+gQcPzhS/sWot3lqxcuTJ33317rrzykmrfx4wZr1f+fP31V2bevPdq9fqre/rpx3L11ZdVe77Pnv1Wnntuyhpjgwf/OIce+p00a9Zwp6FXrFhR7bYb22+/QwFpPtu99/6pyth22/Va489s6dIlmTLlsfX67wYAALYRAaCOde3aNQMHDszAgQNz0EEHFR0HANjE3HzzDZk9+60MG3ZctRfZq3PBBT/PpZdeUKPHrlixIhdffG7OOOPH1V5kX5vf/W50hg49OnPnzqnxcx555K85/vhvVnuRvTqPP/5QjV97dS+/PC2DBn1nrRemqzNt2rMZOPA7uf76K1NWVrZOx91QnAO115jOgY35Xm655ff/e7wjq73w/EmzZ7+VE088NvfdN6nGx9hQHnjgnpx66uBqixarHHDAIfnmN79TZXz1wklNvPTS36scZ/PNW+Vf//VLtXqdunb33bfljjv+kDPPHPGpBZdVnnxycgYPPrLai+7VefnlaTn++G+utWhRnWnTns1JJw2o1fn74YeLc/rpJ+W3vz2/Ru/jrbdm1Ohxn7R8+fJceukF+fnPT67R+b7K2LEX58QTj80//vFCrY9ZX1RXXmjbtl169uxVQJpPV1ZWljvvvHWNse7dt8vAgSdXeey99/55Y8UCWKsjjzyyci55yy3r54pBAJ9G2QKAOtW1a9cMHjw4gwcPVrYAAAoxYMC3anURPEkmTbq5yrdxP2nlypU577z/qfWFxlXeemtGTjzx2Lz//md/o/jBB+/Nueeetk7HqY0nn5yck0/+r0+94PppJkwYl5Ejh2flypV1nGz9OAdqrjGdAxv7vTz33JScfPJ/1fo4o0ZdmEWLFtb6eXVl+vSXc8EFP//Mxx166H/mK1+puuLLCy88l5kz36jx8R555K9Vxr7+9W8VvtLBddeNyeWX/6pWz5k//4OMGfObz3zctGnP5uST/2udSg1JMm7cb3Pjjb/7zMd9+OHijBw5PM888+Q6Haemli1bljPO+HEmTbp5nZ7/+uvTc9JJA/K3vz1Vx8k2vJde+nt++9vzq4yPGHFmrVcq2hgee+yBKufdV7/6jXzxi32qbDX12GMPrvPvS4C6ctRRR1XOJW+xxfptuwdQhIa7fhsAAADUQL9+X8nOO/dOeXl5Zsx4I48//lC1F8AmTLgqu+1WutbXeeCBuyu3A/ikXXfdLV/84r+me/ft8u677+SVV16q9rHz53+QG264KiedtPaL6C+88Fx+8YvT13p/ly7ds8su/5IuXbpl5sw3M3Xq4+t0seSNN/6ZM874cbX3rfrWec+e26dp02Z555238+KLz1fZTiD5eBuPO++8JYccckStM2wszoHqNaZzoL68l4MP/na6dOmWDz9clNdem17taiNLly7JHXf8IUcf/YMq933720dn4cIFWbasLPfcc0eV+w89tOpKE0k+dWuST1p9S4kk6dChU/bcc+9ss822mT79H3n44fuy0069s+OOuyRJvvKVr+XBB+9d4zn3339Xjj/+h595rBUrVlT7Pr7yla/VOO/G0qFDp+y330Fp16595s6dk3/844W88MJzVR43bdqzee65qdlttz2qfZ3ly5fn17/+f9Xe17Ztu+yxx5ez886906xZ87z22qt59tmnq/yZJB+vhvPv//61dO7cpdrXqqioyC9/ObLajKv06bNn5bkxbdqzn/rYTzN69K/WWujYccddssMOO6d9+w5ZunRJXnvt1TW2aVrdr399VsaN+2NatWpV7f1FWLFiRcrKytK8efM0bdo0y5Yty/vvz82cOW/njjv+WO3v8e9857h86Ut7FZD2s/35z1ULMXvv/dVsttlm6d9/v/zlL2uuZvHII/flP/7jPzdWPACARkfZAgAAgEapd+/d89OfnpNOnbZdY3zu3Dn5xS9Or3LR6fnnp+aFF57LrrvuVuW1lixZkiuvvKTa4/z3f5+V/ff/RpXx11+fnp///OS89967a4xPmnRzvvWtI9O9+3bVHOfD/OpXZ1Z7nC5duueMMy5Mz56fX2O8oqIiV199Wf74x+urfV51li1blv/3/0ZUe9+hh34nAwb8MK1br/nNsoqKitxzzx25+OJzqzznsst+mX/7t72yzTada5xhY3AOrF1jOgfqw3s57LDv5rjjBqdVq9ZrjE+b9mzOOee/q5Rh/vjH63P44Udn8803X2P82GMHJfn4PPhkSaFt23b50Y9OrXGmmjjkkCPywx+OWGOViWOOGbjG6h4HHnholbLFnXfemqOPHviZq1NMm/a3KsWmLl265wtf2LUO0tedESPOyH77fb3K+5k06ZZceukvqjx+4sRr11q2mDTp5mpX1tl1191y5pm/Tps2bdcYLy8vz7XXjs4f/jC+ynPGjx+bn/707LUe58knJ1d739FHn5Dvfe8HVd7PzJlv5Cc/GVSrcta99/6p2tV8OnTolBEjzkifPntWue/dd9/JJZecV6V08d5772b8+DEZMuSUGh9/Q3vggbvzwAN31/jxw4b9tN6WC994459V/ru2006987nPdU2S9O+/b5WyxT333KFsAQCwHmwjAgAAQKPzla98LeefP6rKRfYk6dhxm5x55q+rfd4///mPasdvueWGai9OXXjhFdVeZE+S7bbrlVGjrk+XLt2r3HfNNZdX+5wbb/xdtRfp+vX7Si677LoqF9mTpKSkJD/4wUmVF2lr4i9/+VO1xznrrN/kRz86tcqF6VXHOeigb+Z3v7u1ylLkycffwq5PnAOfrjGdA0W/l0GDhmfIkFOqFC2Sjws/I0ZULc8sXbokb789s8bH2BCOPXZQhg37aZUL8j16bJ/ttutVeXu33UrToUOnNR4zf/4HNdq64uGHq24hcuCB/7GOieve5pu3yoUXjsnXvvYf1RZHDj748Bx55PFVxte2csOCBfNz7bVXVBn/8pf/PeefP6pK0SJJmjZtmhNOGJahQ39S5b77778rr776crXHueyyX1b7fs4666Icd9yQat9Pt249c8klv6vy57k2ZWVlufrqy6qM9+69e6666qZqixZJ0qnTtjnvvEvz7W8fXeW+W2/9fd5887UaHb+++c1vxtXbokWSaleROeCAgyt/3n33L1W5/9VXX87rr0/foLkAABozZQsAAAAalX79vpKf/vSctGjRYq2PadOmbQ444JAq43PmzK4y9v77c3P99VdWGT/kkCM+dcuJJGnXbuscddSAKuOPP/5QXnnlpTXGPvxwce64449VHrvddr3y859fUO0F49UddNA3P/X+VT766KNq38+RRx6fvn33+cznf+5zXTN4cNXtGv72t8++8LqxOAc+XWM6B4p+L4MH/7jaC8qr22OPvtVe3J47d06NjrEhdO7cJUcd9V81emzTpk1z8MGHVxn/5DfkP2n58uW5//67qozvs88BNQu5EVx44Zi1rlCxyoEHVv/3atGihVXGJk68ttotioYO/Ulatmz5qcc5+OBvV3ueVHd+T5pUdauIJDn11P+Xvn33/tTjdO7cpUZ/N5Lkvvv+XKVktvnmrXLaaedWWZXlk0pKSnLccUOq3QbllVderNHx65sRIwbmZz8blr/97amio1RRVlaWu+66rcp4v377Vv7csmXL7LPPV6s8pjYrewAAsCZlCwDq1KxZszJ27NiMHTs2d9/tH+wAwMbXtWv3NGny2f/c3XPP/lXG3n33nSpjL73092qff9hh361Rnn32OaDab85/cgWFBx64u9qLdIMGDU/Tpk1rdKyaeOml56tcPGvbtl2OO25IjV/jwAMPrfab7tVdfCyCc+DTNaZzoOj3Ut2qJZ/UtGnT7LXXvlXG33uvuLLFoEHDP3MLkNV99asHVxl7+OH7smDB/LU+529/e6rK+dy79+7VXnwvyqrtFT5Nly7dqv1z/uT2QEnyxBOPVBk74IBDql1h55OaNWtW7e+U55+fusbtFStWVLvlyK677pZ+/b7ymcepjQceuKfK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AABwyfz9/TVr1g8N9TVrCvTuu292+bz29nY9++wvVFS0zbBtzpx8BQYGOtWuvXaK6V2mn3/+sZ588lEdPtx43j7Xr//wvNs7+fv76zvf+b7ptvnz8037Pdf27Vt1//3f1Asv/Kpbr9nX8R4w6i3vAW86l3OZTUlYvvyPvebC9de+Nv2C+1x//U090Envcf31N5kGIR577Ceqr6/r8nm7du3QY489ZKjbbAnKy7vVqebr66s5c/JNj/Pww3NNlyw62+HDjdqy5W/n3afTlCk3mwbN3n9/pV5++Tc6ceLEeZ9//Pgx/f73i5SXN850uk9fUFdXq8WLn9Pzz//SdPuNN369x5fJKS3dp927iwz1G2642eVjTZw42bRuFiwBAE9YunSp43fJx44ds7odAHAZYQsAgFtVVVVpyZIlWrJkCWELAADgNbq6CP7f//2MfvvbX6umpspxAbS9vV2lpfv0i188rPffX2l4TlpahulY/QEDBmjOnAdNX//TTz/Qd74zTatXF6ikZK9aWs6MDj9+/Jg2blyvn//8x/rd757t9vl89atfM72QW1tbrZ/8ZLZeeOFX2rv3S8edrXa7XfX1ddq2bZOefPJRPfzwXJWVFWvlymVuv5O6t+I90HvfA950LmdLT7/MUNu58wv9x3/8WHV1tY5aa2urPv30A7344oKebE/XXnvDBffJzb2uBzrpPc4EIX5kqJeVFWvevG9py5YNjr+70pllYdatW638/PtMl4SYMydfvr6+hvqECZM0atRYQ/3EiRY9+eSjevLJR7Vt2yYdPHhAp0+flt1uV3l5iZYt+1995zvTuh188PPz0/e//7Dptrfffl2zZt2uTz/9QAcO1Oj06dP/6OGESkr2atWqd3TffdP15puvSpLL0zB6Snl5sVavLtBnn32kLVs26LPPPtKf/vSWfv/7RfrP/3xY9957iwoK3jB97rBhI3THHd/u4Y6lv/zlPdN6bu71Lh/r8sv/xXSKzp//bP4aAOBub775puN3yUePHrW6HQBwmfGndQAAAAAA4GTAgAH67nd/oEceMd5B/6c/vaU//ektBQUFKykpVXv3fnneY82d+6AGDhxoum3UqDGaOHGyPv/8Y8O2Eydauryz1lW+vr567LFn9MADM9XUdNiwfeXKZVq58sJLZEjS4sXP6plnXurxO3t7Gu+Brln9HvCmcznb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23omosEXIPWG1dIOAJBNSMfrry9FRUUF9u7diwMHDuDbb78V/H1is9mcJlb09PdNV2lpacjLy3P9EyAiIiIiIiIichGGLYiIiIiIRrG0tDRBrfsEi+5CQ0Nx3XXX4ZprrsGVV17ptKakoNFxOzjU18XdEpGnRUaPge5sHQCgVNf5/R4eHo5bbrkFt9xyC8rKynDgwAFkZ2fj1KlTAAAvLy+nwEXX270JXhARERERERERDUUMWxAREREREQAI3nVufzE0MDAQ06dPR0pKCq644gqkpKT0+AJplbnJcTs8eoz7myaiQSWLuxi2qK9tE1yfOHEibr75Ztx8882orKxEdnY2Pv/8c3zzzTcAnANcXadddL9GRERERERERDTUMWxBRERERDTKdQ1X2AUGBmL+/Pm45pprcPXVV8PXt3dTKhob2h235XFS1zZKRB6nTBqPox+XQgKgraUDHR2Al5f42rCwMKxcuRIrV65EZWUl9u/fj2+++QbffvstKisrATj/vdPTsUVEREREREREREMRwxZERERERAQAkEqlmDt3LhYsWICZM2f2OmBh11jXho6LWQvI1QxbDKb29nYUFHyP3Nyj0Gh+QHm5GVZrEyIioqBSxSM6OhYKRSxUqgSMHx/cqz2PHTuECxdqAQATJoRixow5jmtWqxU5OUeQk3MUZWWlqKgwIyAgEGp1ItTqeKhUCbjyymnw6umV+F7Q63X44ov90Gh+gNlsRH19HSIjZYiOVkKhiMH06XMQGBjU7/2p73z8JPDyAmwdACQSaL+vR3zy2J/8urCwMMfECwDQaDSOqRdFRUUALgYvGLogIiIiIiIiouFAYuNvMYiIiIiIyAXOfV2LT982AQC8fSX4zfokD3c0euTmHsPrr7+AoiJNr9avXn0ffv7zX8LH59L5+9/8ZgkqK8sd99999zOEhITi+PHD2LjxETQ1NV7y66dPn437738UwcETetWXndlswpYtTyE399hPrp05cx6++uoLp1pMjAqvvvpenx6Tei/rhfNorO88QiTtmlDMuWHigPbTaDT4/PPP8dlnn8FgMDjCFnl5eQPulYiIiIiIiIjIXTjZgoiIiIiIXKLcYHXcDvD39mAno0draytefvlf2LNnZ5++7pVXnsW+ff/Dffc9jEmTLuv113322S5IpWPx4ov/7NX6EyeOYPXqX2HDhueRkNC7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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import Image, display\n",
    "from langchain_core.runnables.graph import CurveStyle, MermaidDrawMethod, NodeStyles\n",
    "import nest_asyncio\n",
    "\n",
    "# 修复 asyncio 运行时错误\n",
    "nest_asyncio.apply()\n",
    "\n",
    "png_bytes = app.get_graph().draw_mermaid_png( #  使用 draw_mermaid_png() 方法渲染 PNG 图片\n",
    "    draw_method=MermaidDrawMethod.PYPPETEER, #  指定使用 MermaidDrawMethod.PYPPETEER,  使用 Mermaid + Pyppeteer 渲染\n",
    "    curve_style=CurveStyle.LINEAR, #  设置曲线风格为线性\n",
    "    node_colors=NodeStyles(first=\"#ffdfba\", last=\"#baffc9\", default=\"#fad7de\"), #  自定义节点颜色\n",
    "    wrap_label_n_words=9, #  设置节点标签自动换行，  每行最多 9 个单词\n",
    "    output_file_path=None, #  不输出到文件\n",
    "    background_color=\"white\", #  设置背景色为白色\n",
    "    padding=10, #  设置边距为 10 像素\n",
    ")\n",
    "display(Image(png_bytes)) #  在 Notebook 中显示 PNG 图片"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "je31bve7tv",
   "metadata": {},
   "source": [
    "**💡 图可视化核心价值**：\n",
    "\n",
    "#### 可视化方式对比\n",
    "\n",
    "| 方式 | 优点 | 缺点 | 适用场景 |\n",
    "|------|------|------|----------|\n",
    "| **Mermaid 语法** | 轻量级，无需依赖，跨平台 | 定制化选项有限 | 快速原型，文档嵌入 |\n",
    "| **PNG 图片** | 直观清晰，易于分享 | 无法动态编辑 | 报告展示，静态文档 |\n",
    "| **交互式图表** | 可交互，功能丰富 | 需要额外工具 | 复杂系统分析 |\n",
    "\n",
    "#### 实际应用场景\n",
    "\n",
    "- **开发阶段**：使用 Mermaid 快速验证流程逻辑\n",
    "- **调试阶段**：通过图可视化发现流程问题\n",
    "- **文档阶段**：生成图表用于技术文档和团队分享\n",
    "- **维护阶段**：通过图结构分析优化系统架构\n",
    "\n",
    "图可视化不仅是一个开发辅助工具，更是理解和优化 LangGraph 系统的重要手段，特别是在处理复杂的多智能体系统和子图嵌套场景时发挥重要作用。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "occ4jxydkyr",
   "metadata": {},
   "source": [
    "## 📚 本章总结\n",
    "\n",
    "通过本章的学习，我们深入探讨了 LangGraph 的图驱动 AI 智能体系统，掌握了状态、节点、边、命令四大核心原语的应用，学会了并行处理、MapReduce 模式、子图机制等高级技术，以及工具集成和图可视化等实用开发技能。LangGraph 的图计算模型为我们提供了构建复杂、动态、可扩展智能体系统的强大能力，让我们能够设计出真正适应现实世界复杂场景的 AI 应用。在下一章中，我们将探讨 AI 智能体的交互体验设计，学习如何构建更加人性化、直观易用的智能体界面和交互模式。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
